extHostExtensionService.ts ×64

Frontier kind: Code frontier

unlabeled · c_d1866a379128

33 tests · 81803 LOC · 296 files · introduces 0 tests · 1246 LOC · 16 files

Introduces — evidence that enters the hierarchy at this concept

Code
231 ranges1246 lines · 16 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
5611 ranges81803 lines · 296 files · Browse complete extent
All tests (intent)
33 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

16 files ranked by introduced lines: 1246 introduced LOC across 231 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/api/common/extHostExtensionService.ts 324 introduced LOC · 64 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostExtensionService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import * as nls from '../../../nls.js';
7 > import * as path from '../../../base/common/path.js';
8 > import * as performance from '../../../base/common/performance.js';
9 > import { originalFSPath, joinPath, extUriBiasedIgnorePathCase } from '../../../base/common/resources.js';
10 > import { asPromise, Barrier, IntervalTimer, timeout } from '../../../base/common/async.js';
11 > import { dispose, toDisposable, Disposable, DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
12 > import { TernarySearchTree } from '../../../base/common/ternarySearchTree.js';
13 > import { URI, UriComponents } from '../../../base/common/uri.js';
14 > import { ILogService } from '../../../platform/log/common/log.js';
15 > import { ExtHostExtensionServiceShape, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from './extHost.protocol.js';
16 > import { IExtensionDescriptionDelta, IExtensionHostInitData } from '../../services/extensions/common/extensionHostProtocol.js';
17 > import { ExtHostConfiguration, IExtHostConfiguration } from './extHostConfiguration.js';
18 > import { ActivatedExtension, EmptyExtension, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from './extHostExtensionActivator.js';
19 > import { ExtHostStorage, IExtHostStorage } from './extHostStorage.js';
20 > import { ExtHostWorkspace, IExtHostWorkspace } from './extHostWorkspace.js';
21 > import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled, isProposedApiEnabled, ExtensionActivationReason, IProposedApiUsage, setProposedApiUsageReporter, setEnabledApiProposalsFallbackExperiment } from '../../services/extensions/common/extensions.js';
22 > import { ExtensionDescriptionRegistry, IActivationEventsReader } from '../../services/extensions/common/extensionDescriptionRegistry.js';
23 > import * as errors from '../../../base/common/errors.js';
24 > import type * as vscode from 'vscode';
25 > import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
26 > import { VSBuffer } from '../../../base/common/buffer.js';
27 > import { ExtensionGlobalMemento, ExtensionMemento } from './extHostMemento.js';
28 > import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime, ManagedResolvedAuthority as ExtHostManagedResolvedAuthority } from './extHostTypes.js';
29 > import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData, getRemoteAuthorityPrefix, TunnelInformation, ManagedRemoteConnection, WebSocketRemoteConnection } from '../../../platform/remote/common/remoteAuthorityResolver.js';
30 > import { IInstantiationService, createDecorator } from '../../../platform/instantiation/common/instantiation.js';
31 > import { IExtHostInitDataService } from './extHostInitDataService.js';
32 > import { IExtensionStoragePaths } from './extHostStoragePaths.js';
33 > import { IExtHostRpcService } from './extHostRpcService.js';
34 > import { ServiceCollection } from '../../../platform/instantiation/common/serviceCollection.js';
35 > import { IExtHostTunnelService } from './extHostTunnelService.js';
36 > import { IExtHostTerminalService } from './extHostTerminalService.js';
37 > import { IExtHostLanguageModels } from './extHostLanguageModels.js';
38 > import { Emitter, Event } from '../../../base/common/event.js';
39 > import { IExtensionActivationHost, checkActivateWorkspaceContainsExtension } from '../../services/extensions/common/workspaceContains.js';
40 > import { ExtHostSecretState, IExtHostSecretState } from './extHostSecretState.js';
41 > import { ExtensionSecrets } from './extHostSecrets.js';
42 > import { Schemas } from '../../../base/common/network.js';
43 > import { IResolveAuthorityResult } from '../../services/extensions/common/extensionHostProxy.js';
44 > import { IExtHostLocalizationService } from './extHostLocalizationService.js';
45 > import { StopWatch } from '../../../base/common/stopwatch.js';
46 > import { isCI, setTimeout0 } from '../../../base/common/platform.js';
47 > import { IExtHostManagedSockets } from './extHostManagedSockets.js';
48 > import { Dto } from '../../services/extensions/common/proxyIdentifier.js';
49 >
50 > interface ITestRunner {
51 > /** Old test runner API, as exported from `vscode/lib/testrunner` */
52 > run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
53 > }
54 >
55 > interface INewTestRunner {
56 > /** New test runner API, as explained in the extension test doc */
57 > run(): Promise<void>;
58 > }
59 >
60 > export const IHostUtils = createDecorator<IHostUtils>('IHostUtils');
61 >
62 > export interface IHostUtils {
63 > readonly _serviceBrand: undefined;
64 > readonly pid: number | undefined;
65 > exit(code: number): void;
66 > fsExists?(path: string): Promise<boolean>;
67 > fsRealpath?(path: string): Promise<string>;
68 > }
69 >
70 > type TelemetryActivationEventFragment = {
71 > id: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The identifier of an extension' };
72 > name: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The name of the extension' };
73 > extensionVersion: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The version of the extension' };
74 > publisherDisplayName: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The publisher of the extension' };
75 > activationEvents: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'All activation events of the extension' };
76 > isBuiltin: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'If the extension is builtin or git installed' };
77 > reason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The activation event' };
78 > reasonId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The identifier of the activation event' };
79 > };
80 >
81 > export abstract class AbstractExtHostExtensionService extends Disposable implements ExtHostExtensionServiceShape {
82 >
83 > readonly _serviceBrand: undefined;
84 >
85 > abstract readonly extensionRuntime: ExtensionRuntime;
86 >
87 > private readonly _onDidChangeRemoteConnectionData = this._register(new Emitter<void>());
88 > public readonly onDidChangeRemoteConnectionData = this._onDidChangeRemoteConnectionData.event;
89 >
90 > protected readonly _hostUtils: IHostUtils;
91 > protected readonly _initData: IExtensionHostInitData;
92 > protected readonly _extHostContext: IExtHostRpcService;
93 > protected readonly _instaService: IInstantiationService;
94 > protected readonly _extHostWorkspace: ExtHostWorkspace;
95 > protected readonly _extHostConfiguration: ExtHostConfiguration;
96 > protected readonly _logService: ILogService;
97 > protected readonly _extHostTunnelService: IExtHostTunnelService;
98 > protected readonly _extHostTerminalService: IExtHostTerminalService;
99 > protected readonly _extHostLocalizationService: IExtHostLocalizationService;
100 >
101 > protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape;
102 > protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;
103 > protected readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;
104 >
105 > private readonly _almostReadyToRunExtensions: Barrier;
106 > private readonly _readyToStartExtensionHost: Barrier;
107 > private readonly _readyToRunExtensions: Barrier;
108 > private readonly _eagerExtensionsActivated: Barrier;
109 >
110 > private readonly _activationEventsReader: SyncedActivationEventsReader;
111 > protected readonly _myRegistry: ExtensionDescriptionRegistry;
112 > protected readonly _globalRegistry: ExtensionDescriptionRegistry;
113 > private readonly _storage: ExtHostStorage;
114 > private readonly _secretState: ExtHostSecretState;
115 > private readonly _storagePath: IExtensionStoragePaths;
116 > private readonly _activator: ExtensionsActivator;
117 > private _extensionPathIndex: Promise<ExtensionPaths> | null;
118 > private _realPathCache = new Map<string, Promise<string>>();
119 >
120 > private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver };
121 >
122 > private _started: boolean;
123 > private _isTerminating: boolean = false;
124 > private _remoteConnectionData: IRemoteConnectionData | null;
125 >
126 > constructor(
127 @IInstantiationService instaService: IInstantiationService,
128 @IHostUtils hostUtils: IHostUtils,
212 this._register(setEnabledApiProposalsFallbackExperiment(this._initData.enabledApiProposalsFallback, this._initData.quality));
213 }
215 > private _reportProposedApiUsage(usage: IProposedApiUsage): void {
216 type ProposedApiUsageClassification = {
217 owner: 'alexr00';
229 });
230 }
232 > public getRemoteConnectionData(): IRemoteConnectionData | null {
233 return this._remoteConnectionData;
234 }
236 > public async initialize(): Promise<void> {
237 try {
238
251 }
252 }
254 > private async _deactivateAll(): Promise<void> {
255 this._storagePath.onWillDeactivateAll();
256
269 await Promise.all(allPromises);
270 }
272 > public terminate(reason: string, code: number = 0): void {
273 if (this._isTerminating) {
274 // we are already shutting down...
303 });
304 }
306 > public isActivated(extensionId: ExtensionIdentifier): boolean {
307 if (this._readyToRunExtensions.isOpen()) {
308 return this._activator.isActivated(extensionId);
310 return false;
311 }
313 > public async getExtension(extensionId: string): Promise<IExtensionDescription | undefined> {
314 const ext = await this._mainThreadExtensionsProxy.$getExtension(extensionId);
315 return ext && {
319 };
320 }
322 > private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
323 return this._activator.activateByEvent(activationEvent, startup);
324 }
326 > private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
327 return this._activator.activateById(extensionId, reason);
328 }
330 > public activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
331 return this._activateById(extensionId, reason).then(() => {
332 const extension = this._activator.getActivatedExtension(extensionId);
338 });
339 }
341 > public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
342 return this._readyToRunExtensions.wait().then(_ => this._myRegistry);
343 }
345 > public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined {
346 if (this._readyToRunExtensions.isOpen()) {
347 return this._activator.getActivatedExtension(extensionId).exports;
354 }
355 }
357 > /**
358 > * Applies realpath to file-uris and returns all others uris unmodified.
359 > * The real path is cached for the lifetime of the extension host.
360 > */
361 > private async _realPathExtensionUri(uri: URI): Promise<URI> {
362 if (uri.scheme === Schemas.file && this._hostUtils.fsRealpath) {
363 const fsPath = uri.fsPath;
370 return uri;
371 }
373 > // create trie to enable fast 'filename -> extension id' look up
374 > public async getExtensionPathIndex(): Promise<ExtensionPaths> {
375 if (!this._extensionPathIndex) {
376 this._extensionPathIndex = this._createExtensionPathIndex(this._myRegistry.getAllExtensionDescriptions()).then((searchTree) => {
380 return this._extensionPathIndex;
381 }
383 > /**
384 > * create trie to enable fast 'filename -> extension id' look up
385 > */
386 > private async _createExtensionPathIndex(extensions: IExtensionDescription[]): Promise<TernarySearchTree<URI, IExtensionDescription>> {
387 const tst = TernarySearchTree.forUris<IExtensionDescription>(key => {
388 // using the default/biased extUri-util because the IExtHostFileSystemInfo-service
400 return tst;
401 }
403 > private _deactivate(extensionId: ExtensionIdentifier): Promise<void> {
404 let result = Promise.resolve(undefined);
405
440 return result;
441 }
443 > // --- impl
444 >
445 > private async _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
446 if (!this._initData.remote.isRemote) {
447 // local extension host process
462 });
463 }
465 > private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
466 const event = getTelemetryActivationEvent(extensionDescription, reason);
467 type ExtensionActivationTimesClassification = {
488 });
489 }
491 > private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
492 const event = getTelemetryActivationEvent(extensionDescription, reason);
493 type ActivatePluginClassification = {
522 });
523 }
525 > private _loadExtensionContext(extensionDescription: IExtensionDescription, extensionInternalStore: DisposableStore): Promise<vscode.ExtensionContext> {
526
527 const languageModelAccessInformation = this._extHostLanguageModels.createLanguageModelAccessInformation(extensionDescription);
596 });
597 }
599 > private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, extensionInternalStore: IDisposable, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
600 // Make sure the extension's surface is not undefined
601 extensionModule = extensionModule || {
611 });
612 }
614 > private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
615 if (typeof extensionModule.activate === 'function') {
616 try {
633 }
634 }
636 > // -- eager activation
637 >
638 > private _activateOneStartupFinished(desc: IExtensionDescription, activationEvent: string): void {
639 this._activateById(desc.identifier, {
640 startup: false,
645 });
646 }
648 > private _activateAllStartupFinishedDeferred(extensions: IExtensionDescription[], start: number = 0): void {
649 const timeBudget = 50; // 50 milliseconds
650 const startTime = Date.now();
668 });
669 }
671 > private _activateAllStartupFinished(): void {
672 // startup is considered finished
673 this._mainThreadExtensionsProxy.$setPerformanceMarks(performance.getMarks());
691 });
692 }
694 > // Handle "eager" activation extensions
695 > private _handleEagerExtensions(): Promise<void> {
696 const starActivation = this._activateByEvent('*', true).then(undefined, (err) => {
697 this._logService.error(err);
710 return eagerExtensionsActivation;
711 }
713 > private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray<vscode.WorkspaceFolder>): Promise<void> {
714 if (folders.length === 0) {
715 return Promise.resolve(undefined);
722 ).then(() => { });
723 }
725 > private async _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray<vscode.WorkspaceFolder>, desc: IExtensionDescription): Promise<void> {
726 if (this.isActivated(desc.identifier)) {
727 return;
747 );
748 }
750 > private async _handleRemoteResolverEagerExtensions(): Promise<void> {
751 if (this._initData.remote.authority) {
752 return this._activateByEvent(`onResolveRemoteAuthority:${this._initData.remote.authority}`, false);
753 }
754 }
756 > public async $extensionTestsExecute(): Promise<number> {
757 await this._eagerExtensionsActivated.wait();
758 try {
763 }
764 }
766 > private async _doHandleExtensionTests(): Promise<number> {
767 const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
768 if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI) {
824 });
825 }
827 > private _startExtensionHost(): Promise<void> {
828 if (this._started) {
829 throw new Error(`Extension host is already started!`);
843 });
844 }
846 > // -- called by extensions
847 >
848 > public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable {
849 this._resolvers[authorityPrefix] = resolver;
850 return toDisposable(() => {
852 });
853 }
855 > public async getRemoteExecServer(remoteAuthority: string): Promise<vscode.ExecServer | undefined> {
856 const { resolver } = await this._activateAndGetResolver(remoteAuthority);
857 return resolver?.resolveExecServer?.(remoteAuthority, { resolveAttempt: 0 });
858 }
860 > // -- called by main thread
861 >
862 > private async _activateAndGetResolver(remoteAuthority: string): Promise<{ authorityPrefix: string; resolver: vscode.RemoteAuthorityResolver | undefined }> {
863 const authorityPlusIndex = remoteAuthority.indexOf('+');
864 if (authorityPlusIndex === -1) {
872 return { authorityPrefix, resolver: this._resolvers[authorityPrefix] };
873 }
875 > public async $resolveAuthority(remoteAuthorityChain: string, resolveAttempt: number): Promise<Dto<IResolveAuthorityResult>> {
876 const sw = StopWatch.create(false);
877 const prefix = () => `[resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthorityChain)},${resolveAttempt})][${sw.elapsed()}ms] `;
1003 };
1004 }
1006 > public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents | null> {
1007 this._logService.info(`$getCanonicalURI invoked for authority (${getRemoteAuthorityPrefix(remoteAuthority)})`);
1008
1027 return result;
1028 }
1030 > public async $startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
1031 // eslint-disable-next-line local/code-no-any-casts
1032 extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
1046 return this._startExtensionHost();
1047 }
1049 > public $activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {
1050 if (activationKind === ActivationKind.Immediate) {
1051 return this._almostReadyToRunExtensions.wait()
1058 );
1059 }
1061 > public async $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
1062 await this._readyToRunExtensions.wait();
1063 if (!this._myRegistry.getExtensionDescription(extensionId)) {
1068 return true;
1069 }
1071 > public async $deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
1072 // eslint-disable-next-line local/code-no-any-casts
1073 extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
1088 return Promise.resolve(undefined);
1089 }
1091 > public async $test_latency(n: number): Promise<number> {
1092 return n;
1093 }
1095 > public async $test_up(b: VSBuffer): Promise<number> {
1096 return b.byteLength;
1097 }
1099 > public async $test_down(size: number): Promise<VSBuffer> {
1100 const buff = VSBuffer.alloc(size);
1101 const value = Math.random() % 256;
1105 return buff;
1106 }
1108 > public async $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise<void> {
1109 this._remoteConnectionData = connectionData;
1110 this._onDidChangeRemoteConnectionData.fire();
1111 }
1113 > protected _isESM(extensionDescription: IExtensionDescription | undefined, modulePath?: string): boolean {
1114 modulePath ??= extensionDescription ? this._getEntryPoint(extensionDescription) : modulePath;
1115 return modulePath?.endsWith('.mjs') || (extensionDescription?.type === 'module' && !modulePath?.endsWith('.cjs'));
1116 }
1118 > protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
1119 > protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined;
1120 > protected abstract _loadCommonJSModule<T extends object | undefined>(extensionId: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
1121 > protected abstract _loadESMModule<T>(extension: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
1122 > public abstract $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
1123 > }
1124 >
1125 function applyExtensionsDelta(activationEventsReader: SyncedActivationEventsReader, oldGlobalRegistry: ExtensionDescriptionRegistry, oldMyRegistry: ExtensionDescriptionRegistry, extensionsDelta: IExtensionDescriptionDelta) {
1126 activationEventsReader.addActivationEvents(extensionsDelta.addActivationEvents);
1139 return { globalRegistry, myExtensions };
1140 }
1142 > type TelemetryActivationEvent = {
1143 > id: string;
1144 > name: string;
1145 > extensionVersion: string;
1146 > publisherDisplayName: string;
1147 > activationEvents: string | null;
1148 > isBuiltin: boolean;
1149 > reason: string;
1150 > reasonId: string;
1151 > };
1152 >
1153 function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent {
1154 const event = {
1165 return event;
1166 }
1168 function printExtIds(registry: ExtensionDescriptionRegistry) {
1169 return registry.getAllExtensionDescriptions().map(ext => ext.identifier.value).join(',');
1170 }
1172 > export const IExtHostExtensionService = createDecorator<IExtHostExtensionService>('IExtHostExtensionService');
1173 >
1174 > export interface IExtHostExtensionService extends AbstractExtHostExtensionService {
1175 > readonly _serviceBrand: undefined;
1176 > initialize(): Promise<void>;
1177 > terminate(reason: string): void;
1178 > getExtension(extensionId: string): Promise<IExtensionDescription | undefined>;
1179 > isActivated(extensionId: ExtensionIdentifier): boolean;
1180 > activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
1181 > getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined;
1182 > getExtensionRegistry(): Promise<ExtensionDescriptionRegistry>;
1183 > getExtensionPathIndex(): Promise<ExtensionPaths>;
1184 > registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable;
1185 > getRemoteExecServer(authority: string): Promise<vscode.ExecServer | undefined>;
1186 >
1187 > readonly onDidChangeRemoteConnectionData: Event<void>;
1188 > getRemoteConnectionData(): IRemoteConnectionData | null;
1189 > }
1190 >
1191 > export class Extension<T extends object | null | undefined> implements vscode.Extension<T> {
1192 >
1193 > #extensionService: IExtHostExtensionService;
1194 #originExtensionId: ExtensionIdentifier;
1195 #identifier: ExtensionIdentifier;
1197 > readonly id: string;
1198 > readonly extensionUri: URI;
1199 > readonly extensionPath: string;
1200 > readonly packageJSON: IExtensionDescription;
1201 > readonly extensionKind: vscode.ExtensionKind;
1202 > readonly isFromDifferentExtensionHost: boolean;
1203 >
1204 > constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind, isFromDifferentExtensionHost: boolean) {
1205 this.#extensionService = extensionService;
1206 this.#originExtensionId = originExtensionId;
1213 this.isFromDifferentExtensionHost = isFromDifferentExtensionHost;
1214 }
1216 > get isActive(): boolean {
1217 // TODO@alexdima support this
1218 return this.#extensionService.isActivated(this.#identifier);
1219 }
1221 > get exports(): T {
1222 if (this.packageJSON.api === 'none' || this.isFromDifferentExtensionHost) {
1223 return undefined!; // Strict nulloverride - Public api
1225 return <T>this.#extensionService.getExtensionExports(this.#identifier);
1226 }
1228 > async activate(): Promise<T> {
1229 if (this.isFromDifferentExtensionHost) {
1230 throw new Error('Cannot activate foreign extension'); // TODO@alexdima support this
1233 return this.exports;
1234 }
1236 >
1237 function filterExtensions(globalRegistry: ExtensionDescriptionRegistry, desiredExtensions: ExtensionIdentifierSet): IExtensionDescription[] {
1238 return globalRegistry.getAllExtensionDescriptions().filter(
1240 );
1241 }
1243 > export class ExtensionPaths {
1244 >
1245 > constructor(
1246 private _searchTree: TernarySearchTree<URI, IExtensionDescription>
1247 ) { }
1249 > setSearchTree(searchTree: TernarySearchTree<URI, IExtensionDescription>): void {
1250 this._searchTree = searchTree;
1251 }
1253 > findSubstr(key: URI): IExtensionDescription | undefined {
1254 return this._searchTree.findSubstr(key);
1255 }
1257 > forEach(callback: (value: IExtensionDescription, index: URI) => any): void {
1258 return this._searchTree.forEach(callback);
1259 }
1261 >
1262 > /**
1263 > * This mirrors the activation events as seen by the renderer. The renderer
1264 > * is the only one which can have a reliable view of activation events because
1265 > * implicit activation events are generated via extension points, and they
1266 > * are registered only on the renderer side.
1267 > */
1268 > class SyncedActivationEventsReader implements IActivationEventsReader {
1269 >
1270 > private readonly _map = new ExtensionIdentifierMap<string[]>();
1271 >
1272 > constructor(activationEvents: { [extensionId: string]: string[] }) {
1273 this.addActivationEvents(activationEvents);
1274 }
1276 > public readActivationEvents(extensionDescription: IExtensionDescription): string[] {
1277 return this._map.get(extensionDescription.identifier) ?? [];
1278 }
1280 > public addActivationEvents(activationEvents: { [extensionId: string]: string[] }): void {
1281 for (const extensionId of Object.keys(activationEvents)) {
1282 this._map.set(extensionId, activationEvents[extensionId]);
1283 }
1284 }
src/vs/workbench/api/common/extHostAuthentication.ts 180 introduced LOC · 39 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostAuthentication.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as vscode from 'vscode';
7 > import * as nls from '../../../nls.js';
8 > import { Emitter, Event } from '../../../base/common/event.js';
9 > import { MainContext, MainThreadAuthenticationShape, ExtHostAuthenticationShape } from './extHost.protocol.js';
10 > import { Disposable, ProgressLocation } from './extHostTypes.js';
11 > import { IExtensionDescription, ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js';
12 > import { IAuthenticationGetSessionsOptions, IAuthenticationProviderSessionOptions, INTERNAL_AUTH_PROVIDER_PREFIX, isAuthenticationWwwAuthenticateRequest } from '../../services/authentication/common/authentication.js';
13 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
14 > import { IExtHostRpcService } from './extHostRpcService.js';
15 > import { URI, UriComponents } from '../../../base/common/uri.js';
16 > import { AuthorizationErrorType, fetchDynamicRegistration, getClaimsFromJWT, IAuthorizationJWTClaims, IAuthorizationProtectedResourceMetadata, IAuthorizationServerMetadata, IAuthorizationTokenResponse, isAuthorizationErrorResponse, isAuthorizationTokenResponse } from '../../../base/common/oauth.js';
17 > import { IExtHostWindow } from './extHostWindow.js';
18 > import { IExtHostInitDataService } from './extHostInitDataService.js';
19 > import { ILogger, ILoggerService, ILogService } from '../../../platform/log/common/log.js';
20 > import { autorun, derivedOpts, IObservable, ISettableObservable, observableValue } from '../../../base/common/observable.js';
21 > import { stringHash } from '../../../base/common/hash.js';
22 > import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
23 > import { IExtHostUrlsService } from './extHostUrls.js';
24 > import { encodeBase64, VSBuffer } from '../../../base/common/buffer.js';
25 > import { equals as arraysEqual } from '../../../base/common/arrays.js';
26 > import { IExtHostProgress } from './extHostProgress.js';
27 > import { IProgressStep } from '../../../platform/progress/common/progress.js';
28 > import { CancellationError, isCancellationError } from '../../../base/common/errors.js';
29 > import { raceCancellationError, SequencerByKey } from '../../../base/common/async.js';
30 > import { XaaifyAuthProvider } from './extHostXaaAuthProvider.js';
31 >
32 > export interface IExtHostAuthentication extends ExtHostAuthentication { }
33 > export const IExtHostAuthentication = createDecorator<IExtHostAuthentication>('IExtHostAuthentication');
34 >
35 > interface ProviderWithMetadata {
36 > label: string;
37 > provider: vscode.AuthenticationProvider;
38 > disposable?: vscode.Disposable;
39 > options: vscode.AuthenticationProviderOptions;
40 > }
41 >
42 > export class ExtHostAuthentication implements ExtHostAuthenticationShape {
43 >
44 > declare _serviceBrand: undefined;
45 >
46 > protected readonly _dynamicAuthProviderCtor = DynamicAuthProvider;
47 > protected readonly _xaaAuthProviderCtor = XaaifyAuthProvider(DynamicAuthProvider);
48 >
49 > private _proxy: MainThreadAuthenticationShape;
50 > private _authenticationProviders: Map<string, ProviderWithMetadata> = new Map<string, ProviderWithMetadata>();
51 > private _providerOperations = new SequencerByKey<string>();
52 >
53 > private _onDidChangeSessions = new Emitter<vscode.AuthenticationSessionsChangeEvent & { extensionIdFilter?: string[] }>();
54 > private _getSessionTaskSingler = new TaskSingler<vscode.AuthenticationSession | undefined>();
55 >
56 > private _onDidDynamicAuthProviderTokensChange = new Emitter<{ authProviderId: string; clientId: string; tokens: IAuthorizationToken[] }>();
57 >
58 > constructor(
59 @IExtHostRpcService extHostRpc: IExtHostRpcService,
60 @IExtHostInitDataService private readonly _initData: IExtHostInitDataService,
67 this._proxy = extHostRpc.getProxy(MainContext.MainThreadAuthentication);
68 }
70 > /**
71 > * This sets up an event that will fire when the auth sessions change with a built-in filter for the extensionId
72 > * if a session change only affects a specific extension.
73 > * @param extensionId The extension that is interested in the event.
74 > * @returns An event with a built-in filter for the extensionId
75 > */
76 > getExtensionScopedSessionsEvent(extensionId: string): Event<vscode.AuthenticationSessionsChangeEvent> {
77 const normalizedExtensionId = extensionId.toLowerCase();
78 return Event.chain(this._onDidChangeSessions.event, ($) => $
81 );
82 }
84 > async getSession(requestingExtension: IExtensionDescription, providerId: string, scopesOrRequest: readonly string[] | vscode.AuthenticationWwwAuthenticateRequest, options: vscode.AuthenticationGetSessionOptions & ({ createIfNone: true } | { forceNewSession: true } | { forceNewSession: vscode.AuthenticationForceNewSessionOptions })): Promise<vscode.AuthenticationSession>;
85 > async getSession(requestingExtension: IExtensionDescription, providerId: string, scopesOrRequest: readonly string[] | vscode.AuthenticationWwwAuthenticateRequest, options: vscode.AuthenticationGetSessionOptions & { forceNewSession: true }): Promise<vscode.AuthenticationSession>;
86 > async getSession(requestingExtension: IExtensionDescription, providerId: string, scopesOrRequest: readonly string[] | vscode.AuthenticationWwwAuthenticateRequest, options: vscode.AuthenticationGetSessionOptions & { forceNewSession: vscode.AuthenticationForceNewSessionOptions }): Promise<vscode.AuthenticationSession>;
87 > async getSession(requestingExtension: IExtensionDescription, providerId: string, scopesOrRequest: readonly string[] | vscode.AuthenticationWwwAuthenticateRequest, options: vscode.AuthenticationGetSessionOptions): Promise<vscode.AuthenticationSession | undefined>;
88 > async getSession(requestingExtension: IExtensionDescription, providerId: string, scopesOrRequest: readonly string[] | vscode.AuthenticationWwwAuthenticateRequest, options: vscode.AuthenticationGetSessionOptions = {}): Promise<vscode.AuthenticationSession | undefined> {
89 const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier);
90 const keys: (keyof vscode.AuthenticationGetSessionOptions)[] = Object.keys(options) as (keyof vscode.AuthenticationGetSessionOptions)[];
128 });
129 }
131 > async getAccounts(providerId: string) {
132 await this._proxy.$ensureProvider(providerId);
133 return await this._proxy.$getAccounts(providerId);
134 }
136 > registerAuthenticationProvider(id: string, label: string, provider: vscode.AuthenticationProvider, options?: vscode.AuthenticationProviderOptions): vscode.Disposable {
137 // register
138 void this._providerOperations.queue(id, async () => {
167 });
168 }
170 > $createSession(providerId: string, scopes: string[], options: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession> {
171 return this._providerOperations.queue(providerId, async () => {
172 const providerData = this._authenticationProviders.get(providerId);
179 });
180 }
182 > $removeSession(providerId: string, sessionId: string): Promise<void> {
183 return this._providerOperations.queue(providerId, async () => {
184 const providerData = this._authenticationProviders.get(providerId);
190 });
191 }
193 > $getSessions(providerId: string, scopes: ReadonlyArray<string> | undefined, options: IAuthenticationGetSessionsOptions): Promise<ReadonlyArray<vscode.AuthenticationSession>> {
194 return this._providerOperations.queue(providerId, async () => {
195 const providerData = this._authenticationProviders.get(providerId);
202 });
203 }
205 > $getSessionsFromChallenges(providerId: string, constraint: vscode.AuthenticationConstraint, options: vscode.AuthenticationProviderSessionOptions): Promise<ReadonlyArray<vscode.AuthenticationSession>> {
206 return this._providerOperations.queue(providerId, async () => {
207 const providerData = this._authenticationProviders.get(providerId);
219 });
220 }
222 > $createSessionFromChallenges(providerId: string, constraint: vscode.AuthenticationConstraint, options: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession> {
223 return this._providerOperations.queue(providerId, async () => {
224 const providerData = this._authenticationProviders.get(providerId);
236 });
237 }
239 > $onDidChangeAuthenticationSessions(id: string, label: string, extensionIdFilter?: string[]) {
240 // Don't fire events for the internal auth providers
241 if (!id.startsWith(INTERNAL_AUTH_PROVIDER_PREFIX)) {
244 return Promise.resolve();
245 }
247 > $onDidUnregisterAuthenticationProvider(id: string): Promise<void> {
248 return this._providerOperations.queue(id, async () => {
249 const providerData = this._authenticationProviders.get(id);
254 });
255 }
257 > async $registerDynamicAuthProvider(
258 authorizationServerComponents: UriComponents,
259 serverMetadata: IAuthorizationServerMetadata,
343 return provider.id;
344 }
346 > async $registerXaaAuthProvider(
347 issuerComponents: UriComponents,
348 serverMetadata: IAuthorizationServerMetadata,
412 return provider.id;
413 }
415 > async $onDidChangeDynamicAuthProviderTokens(authProviderId: string, clientId: string, tokens: IAuthorizationToken[]): Promise<void> {
416 this._onDidDynamicAuthProviderTokensChange.fire({ authProviderId, clientId, tokens });
417 }
419 >
420 class TaskSingler<T> {
421 private _inFlightPromises = new Map<string, Promise<T>>();
422 > getOrCreate(key: string, promiseFactory: () => Promise<T>) { extHostAuthentication.ts
423 const inFlight = this._inFlightPromises.get(key);
424 if (inFlight) {
431 return promise;
432 }
434 >
435 > export class DynamicAuthProvider implements vscode.AuthenticationProvider {
436 > id: string;
437 > readonly label: string;
438 >
439 > private _onDidChangeSessions = new Emitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
440 > readonly onDidChangeSessions = this._onDidChangeSessions.event;
441 >
442 > private readonly _onDidChangeClientId = new Emitter<void>();
443 > readonly onDidChangeClientId = this._onDidChangeClientId.event;
444 >
445 > private readonly _tokenStore: TokenStore;
446 >
447 > protected readonly _createFlows: Array<{
448 > label: string;
449 > handler: (scopes: string[], progress: vscode.Progress<{ message: string }>, token: vscode.CancellationToken) => Promise<IAuthorizationTokenResponse>;
450 > }>;
451 >
452 > protected readonly _logger: ILogger;
453 > private readonly _disposable: DisposableStore;
454 >
455 > constructor(
456 @IExtHostWindow protected readonly _extHostWindow: IExtHostWindow,
457 @IExtHostUrlsService protected readonly _extHostUrls: IExtHostUrlsService,
503 }
504 }
506 > get clientId(): string {
507 return this._clientId;
508 }
510 > get clientSecret(): string | undefined {
511 return this._clientSecret;
512 }
514 > async getSessions(scopes: readonly string[] | undefined, options: IAuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession[]> {
515 this._logger.info(`Getting sessions for scopes: ${scopes?.join(' ') ?? 'all'}`);
516 if (!scopes) {
570 return [];
571 }
573 > async createSession(scopes: string[], _options: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession> {
574 this._logger.info(`Creating session for scopes: ${scopes.join(' ')}`);
575 let token: IAuthorizationTokenResponse | undefined;
618 return session;
619 }
621 > async removeSession(sessionId: string): Promise<void> {
622 this._logger.info(`Removing session with id: ${sessionId}`);
623 const session = this._tokenStore.sessions.find(session => session.id === sessionId);
634 this._logger.info(`Removed token for session: ${session.id} with scopes: ${session.scopes.join(' ')}`);
635 }
637 > dispose(): void {
638 this._disposable.dispose();
639 }
641 > private async _createWithUrlHandler(scopes: string[], progress: vscode.Progress<IProgressStep>, token: vscode.CancellationToken): Promise<IAuthorizationTokenResponse> {
642 if (!this._serverMetadata.authorization_endpoint) {
643 throw new Error('Authorization Endpoint required');
714 return tokenResponse;
715 }
717 > protected generateRandomString(length: number): string {
718 const array = new Uint8Array(length);
719 crypto.getRandomValues(array);
723 .substring(0, length);
724 }
726 > protected async generateCodeChallenge(codeVerifier: string): Promise<string> {
727 const encoder = new TextEncoder();
728 const data = encoder.encode(codeVerifier);
735 .replace(/=+$/, '');
736 }
738 > private async waitForAuthorizationCode(expectedState: URI): Promise<{ code: string }> {
739 const result = await this._proxy.$waitForUriHandler(expectedState);
740 // Extract the code parameter directly from the query string. NOTE, URLSearchParams does not work here because
747 return { code: codeMatch[1] };
748 }
750 > protected async exchangeCodeForToken(code: string, codeVerifier: string, redirectUri: string): Promise<IAuthorizationTokenResponse> {
751 if (!this._serverMetadata.token_endpoint) {
752 throw new Error('Token endpoint not available in server metadata');
804 throw new Error(`Invalid authorization token response: ${JSON.stringify(result)}`);
805 }
807 > protected async exchangeRefreshTokenForToken(refreshToken: string, allowClientRegistration: boolean): Promise<IAuthorizationToken> {
808 if (!this._serverMetadata.token_endpoint) {
809 throw new Error('Token endpoint not available in server metadata');
851 throw new Error(`Invalid authorization token response: ${JSON.stringify(result)}`);
852 }
854 > protected async _generateNewClientId(): Promise<void> {
855 try {
856 const registration = await fetchDynamicRegistration(this._serverMetadata, this._initData.environment.appName, this._resourceMetadata?.scopes_supported);
883 }
884 }
886 >
887 > export type IAuthorizationToken = IAuthorizationTokenResponse & {
888 > /**
889 > * The time when the token was created, in milliseconds since the epoch.
890 > */
891 > created_at: number;
892 > };
893 >
894 > export class TokenStore implements Disposable {
895 > private readonly _tokensObservable: ISettableObservable<IAuthorizationToken[]>;
896 > private readonly _sessionsObservable: IObservable<vscode.AuthenticationSession[]>;
897 >
898 > private readonly _onDidChangeSessions = new Emitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
899 > readonly onDidChangeSessions = this._onDidChangeSessions.event;
900 >
901 > private readonly _disposable: DisposableStore;
902 >
903 > constructor(
904 private readonly _persistence: { onDidChange: Event<IAuthorizationToken[]>; set: (tokens: IAuthorizationToken[]) => void },
905 initialTokens: IAuthorizationToken[],
915 this._disposable.add(this._persistence.onDidChange((tokens) => this._tokensObservable.set(tokens, undefined)));
916 }
918 > get tokens(): IAuthorizationToken[] {
919 return this._tokensObservable.get();
920 }
922 > get sessions(): vscode.AuthenticationSession[] {
923 return this._sessionsObservable.get();
924 }
926 > dispose() {
927 this._disposable.dispose();
928 }
930 > update({ added, removed }: { added: IAuthorizationToken[]; removed: IAuthorizationToken[] }): void {
931 this._logger.trace(`Updating tokens: added ${added.length}, removed ${removed.length}`);
932 const currentTokens = [...this._tokensObservable.get()];
951 this._logger.trace(`Tokens updated: ${currentTokens.length} tokens stored.`);
952 }
954 > private _registerChangeEventAutorun(): IDisposable {
955 let previousSessions: vscode.AuthenticationSession[] = [];
956 return autorun((reader) => {
1005 });
1006 }
1008 > private _getSessionFromToken(token: IAuthorizationTokenResponse): vscode.AuthenticationSession {
1009 let claims: IAuthorizationJWTClaims | undefined;
1010 if (token.id_token) {
src/vs/workbench/api/common/extHostLanguageModels.ts 149 introduced LOC · 35 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostLanguageModels.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as vscode from 'vscode';
7 > import { AsyncIterableProducer, AsyncIterableSource, RunOnceScheduler } from '../../../base/common/async.js';
8 > import { VSBuffer } from '../../../base/common/buffer.js';
9 > import { CancellationToken, CancellationTokenSource } from '../../../base/common/cancellation.js';
10 > import { SerializedError, transformErrorForSerialization, transformErrorFromSerialization } from '../../../base/common/errors.js';
11 > import { Emitter, Event } from '../../../base/common/event.js';
12 > import { Iterable } from '../../../base/common/iterator.js';
13 > import { DisposableMap, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
14 > import { IJSONSchema } from '../../../base/common/jsonSchema.js';
15 > import { URI, UriComponents } from '../../../base/common/uri.js';
16 > import { localize } from '../../../nls.js';
17 > import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
18 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
19 > import { ILogService } from '../../../platform/log/common/log.js';
20 > import { Progress } from '../../../platform/progress/common/progress.js';
21 > import { COPILOT_VENDOR_ID, IChatMessage, IChatResponsePart, ILanguageModelChatInfoOptions, ILanguageModelChatMetadata, ILanguageModelChatMetadataAndIdentifier, ILanguageModelChatRequestOptions } from '../../contrib/chat/common/languageModels.js';
22 > import { INTERNAL_AUTH_PROVIDER_PREFIX } from '../../services/authentication/common/authentication.js';
23 > import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js';
24 > import { SerializableObjectWithBuffers } from '../../services/extensions/common/proxyIdentifier.js';
25 > import { ExtHostLanguageModelsShape, MainContext, MainThreadLanguageModelsShape } from './extHost.protocol.js';
26 > import { IExtHostAuthentication } from './extHostAuthentication.js';
27 > import { IExtHostRpcService } from './extHostRpcService.js';
28 > import * as typeConvert from './extHostTypeConverters.js';
29 > import * as extHostTypes from './extHostTypes.js';
30 > import { ChatAgentLocation } from '../../contrib/chat/common/constants.js';
31 >
32 > export interface IExtHostLanguageModels extends ExtHostLanguageModels { }
33 >
34 > export const IExtHostLanguageModels = createDecorator<IExtHostLanguageModels>('IExtHostLanguageModels');
35 >
36 > type LanguageModelProviderData = {
37 > readonly extension: IExtensionDescription;
38 > readonly provider: vscode.LanguageModelChatProvider;
39 > };
40 >
41 > type LMResponsePart = vscode.LanguageModelTextPart | vscode.LanguageModelToolCallPart | vscode.LanguageModelDataPart | vscode.LanguageModelThinkingPart;
42 >
43 >
44 > class LanguageModelResponse {
45 >
46 > readonly apiObject: vscode.LanguageModelChatResponse;
47 >
48 > private readonly _defaultStream = new AsyncIterableSource<LMResponsePart>();
49 > private _isDone: boolean = false;
50 >
51 > constructor() {
52
53 const that = this;
71 };
72 }
74 > handleResponsePart(parts: IChatResponsePart | IChatResponsePart[]): void {
75 if (this._isDone) {
76 return;
97 this._defaultStream.emitMany(lmResponseParts);
98 }
100 > reject(err: Error): void {
101 this._isDone = true;
102 this._defaultStream.reject(err);
103 }
105 > resolve(): void {
106 this._isDone = true;
107 this._defaultStream.resolve();
108 }
110 >
111 > export class ExtHostLanguageModels implements ExtHostLanguageModelsShape {
112 >
113 > declare _serviceBrand: undefined;
114 >
115 > private static _idPool = 1;
116 >
117 > private readonly _proxy: MainThreadLanguageModelsShape;
118 > private readonly _onDidChangeModelAccess = new Emitter<{ from: ExtensionIdentifier; to: ExtensionIdentifier }>();
119 > private readonly _onDidChangeProviders = new Emitter<void>();
120 > readonly onDidChangeProviders = this._onDidChangeProviders.event;
121 > private readonly _onDidChangeModelProxyAvailability = new Emitter<void>();
122 > readonly onDidChangeModelProxyAvailability = this._onDidChangeModelProxyAvailability.event;
123 >
124 > private readonly _languageModelProviders = new Map<string, LanguageModelProviderData>();
125 > // TODO @lramos15 - Remove the need for both info and metadata as it's a lot of redundancy. Should just need one
126 > private readonly _localModels = new Map<string, { group: string | undefined; metadata: ILanguageModelChatMetadata; info: vscode.LanguageModelChatInformation }>();
127 > private readonly _modelAccessList = new ExtensionIdentifierMap<ExtensionIdentifierSet>();
128 > private readonly _pendingRequest = new Map<number, { languageModelId: string; res: LanguageModelResponse }>();
129 > private readonly _pendingCancelCTS = new DisposableMap<number, CancellationTokenSource>();
130 > private readonly _ignoredFileProviders = new Map<number, vscode.LanguageModelIgnoredFileProvider>();
131 > private _languageModelProxyProvider: vscode.LanguageModelProxyProvider | undefined;
132 >
133 > constructor(
134 @IExtHostRpcService extHostRpc: IExtHostRpcService,
135 @ILogService private readonly _logService: ILogService,
138 this._proxy = extHostRpc.getProxy(MainContext.MainThreadLanguageModels);
139 }
141 > dispose(): void {
142 this._onDidChangeModelAccess.dispose();
143 this._onDidChangeProviders.dispose();
146 this._pendingCancelCTS.dispose();
147 }
149 > registerLanguageModelChatProvider(extension: IExtensionDescription, vendor: string, provider: vscode.LanguageModelChatProvider): IDisposable {
150
151 this._languageModelProviders.set(vendor, { extension: extension, provider });
170 });
171 }
173 > private toModelIdentifier(vendor: string, group: string | undefined, modelId: string): string {
174 return group ? `${vendor}/${group}/${modelId}` : `${vendor}/${modelId}`;
175 }
177 > private getVendorFromModelIdentifier(modelIdentifier: string): string | undefined {
178 const firstSlash = modelIdentifier.indexOf('/');
179 return firstSlash === -1 ? undefined : modelIdentifier.substring(0, firstSlash);
180 }
182 > async $provideLanguageModelChatInfo(vendor: string, options: ILanguageModelChatInfoOptions, token: CancellationToken): Promise<ILanguageModelChatMetadataAndIdentifier[]> {
183 const data = this._languageModelProviders.get(vendor);
184 if (!data) {
274 return modelMetadataAndIdentifier;
275 }
277 > async $startChatRequest(modelId: string, requestId: number, from: ExtensionIdentifier | undefined, messages: SerializableObjectWithBuffers<IChatMessage[]>, options: ILanguageModelChatRequestOptions, token: CancellationToken): Promise<void> {
278 const knownModel = this._localModels.get(modelId);
279 if (!knownModel) {
366 });
367 }
369 > //#region --- token counting
370 >
371 > $cancelLanguageModelChatRequest(requestId: number): void {
372 this._pendingCancelCTS.get(requestId)?.cancel();
373 }
375 > $provideTokenLength(modelId: string, value: string, token: CancellationToken): Promise<number> {
376 const knownModel = this._localModels.get(modelId);
377 if (!knownModel) {
384 return Promise.resolve(data.provider.provideTokenCount(knownModel.info, value, token));
385 }
387 >
388 > //#region --- making request
389 >
390 > async getDefaultLanguageModel(extension: IExtensionDescription, forceResolveModels?: boolean): Promise<vscode.LanguageModelChat | undefined> {
391 let defaultModelId: string | undefined;
392
407 return this.getLanguageModelByIdentifier(extension, defaultModelId);
408 }
410 > async getLanguageModelByIdentifier(extension: IExtensionDescription, modelId: string | undefined): Promise<vscode.LanguageModelChat | undefined> {
411 if (!modelId) {
412 return undefined;
431 return this._createLanguageModelChatApi(extension, modelId);
432 }
434 > private async _createLanguageModelChatApi(extension: IExtensionDescription, modelId: string): Promise<vscode.LanguageModelChat | undefined> {
435 const model = this._localModels.get(modelId);
436 if (!model) {
484 return apiObject;
485 }
487 > async selectLanguageModels(extension: IExtensionDescription, selector: vscode.LanguageModelChatSelector) {
488
489 // this triggers extension activation
494 return modelResults.filter((m): m is vscode.LanguageModelChat => !!m);
495 }
497 > private async _sendChatRequest(extension: IExtensionDescription, languageModelId: string, messages: vscode.LanguageModelChatMessage2[], options: vscode.LanguageModelChatRequestOptions, token: CancellationToken) {
498
499 const internalMessages: IChatMessage[] = this._convertMessages(extension, messages);
537 return res.apiObject;
538 }
540 > private _convertMessages(extension: IExtensionDescription, messages: vscode.LanguageModelChatMessage2[]) {
541 const internalMessages: IChatMessage[] = [];
542 for (const message of messages) {
548 return internalMessages;
549 }
551 > async $acceptResponsePart(requestId: number, chunk: SerializableObjectWithBuffers<IChatResponsePart | IChatResponsePart[]>): Promise<void> {
552 const data = this._pendingRequest.get(requestId);
553 if (data) {
555 }
556 }
558 > $onChatModelsChange(): void {
559 this._onDidChangeProviders.fire();
560 }
562 > async $acceptResponseDone(requestId: number, error: SerializedError | undefined): Promise<void> {
563 const data = this._pendingRequest.get(requestId);
564 if (!data) {
575 }
576 }
578 > // BIG HACK: Using AuthenticationProviders to check access to Language Models
579 > private async _getAuthAccess(from: IExtensionDescription, to: { identifier: ExtensionIdentifier; displayName: string }, justification: string | undefined, silent: boolean | undefined): Promise<boolean> {
580 // This needs to be done in both MainThread & ExtHost ChatProvider
581 const providerId = INTERNAL_AUTH_PROVIDER_PREFIX + to.identifier.value;
604 }
605 }
607 > private _isUsingAuth(from: ExtensionIdentifier, toMetadata: ILanguageModelChatMetadata): toMetadata is ILanguageModelChatMetadata & { auth: NonNullable<ILanguageModelChatMetadata['auth']> } {
608 // If the 'to' extension uses an auth check
609 return !!toMetadata.auth
611 && !ExtensionIdentifier.equals(toMetadata.extension, from);
612 }
614 > private async _fakeAuthPopulate(metadata: ILanguageModelChatMetadata): Promise<void> {
615
616 if (!metadata.auth) {
627 }
628 }
630 > private async _computeTokenLength(modelId: string, value: string | vscode.LanguageModelChatMessage2, token: vscode.CancellationToken): Promise<number> {
631
632 const data = this._localModels.get(modelId);
637 // return this._proxy.$countTokens(languageModelId, (typeof value === 'string' ? value : typeConvert.LanguageModelChatMessage2.from(value)), token);
638 }
640 > $updateModelAccesslist(data: { from: ExtensionIdentifier; to: ExtensionIdentifier; enabled: boolean }[]): void {
641 const updated = new Array<{ from: ExtensionIdentifier; to: ExtensionIdentifier }>();
642 for (const { from, to, enabled } of data) {
656 }
657 }
659 > private readonly _languageAccessInformationExtensions = new Set<Readonly<IExtensionDescription>>();
660 >
661 > createLanguageModelAccessInformation(from: Readonly<IExtensionDescription>): vscode.LanguageModelAccessInformation {
662
663 this._languageAccessInformationExtensions.add(from);
700 };
701 }
703 > fileIsIgnored(extension: IExtensionDescription, uri: vscode.Uri, token: vscode.CancellationToken = CancellationToken.None): Promise<boolean> {
704 checkProposedApiEnabled(extension, 'chatParticipantAdditions');
705
706 return this._proxy.$fileIsIgnored(uri, token);
707 }
709 > get isModelProxyAvailable(): boolean {
710 return !!this._languageModelProxyProvider;
711 }
713 > async getModelProxy(extension: IExtensionDescription): Promise<vscode.LanguageModelProxy> {
714 checkProposedApiEnabled(extension, 'languageModelProxy');
715
732 }
733 }
735 > async $isFileIgnored(handle: number, uri: UriComponents, token: CancellationToken): Promise<boolean> {
736 const provider = this._ignoredFileProviders.get(handle);
737 if (!provider) {
741 return (await provider.provideFileIgnored(URI.revive(uri), token)) ?? false;
742 }
744 > registerIgnoredFileProvider(extension: IExtensionDescription, provider: vscode.LanguageModelIgnoredFileProvider): vscode.Disposable {
745 checkProposedApiEnabled(extension, 'chatParticipantPrivate');
746
753 });
754 }
756 > registerLanguageModelProxyProvider(extension: IExtensionDescription, provider: vscode.LanguageModelProxyProvider): vscode.Disposable {
757 checkProposedApiEnabled(extension, 'chatParticipantPrivate');
758
src/vs/workbench/api/common/extHostXaaAuthProvider.ts 83 introduced LOC · 5 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostXaaAuthProvider.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as vscode from 'vscode';
7 > import { stringHash } from '../../../base/common/hash.js';
8 > import { buildIdJagExchangeBody, buildResourceRedemptionBody, fetchAuthorizationServerMetadata, getClaimsFromJWT, IAuthorizationJWTClaims, IAuthorizationTokenResponse, isAuthorizationTokenResponse } from '../../../base/common/oauth.js';
9 > import { DynamicAuthProvider } from './extHostAuthentication.js';
10 >
11 > // eslint-disable-next-line @typescript-eslint/no-explicit-any
12 > type Ctor<T> = new (...args: any[]) => T;
13 >
14 > /**
15 > * Scopes used when bootstrapping the IdP session for an XAA flow.
16 > *
17 > * `openid` is required because the ID-JAG token exchange uses the IdP-issued
18 > * `id_token` as `subject_token` (per draft-ietf-oauth-identity-assertion-authz-grant
19 > * section 3.1, the subject token MUST be of type `urn:ietf:params:oauth:token-type:id_token`).
20 > * `offline_access` is requested so we get a refresh token for the IdP session.
21 > */
22 > export const IDP_SCOPES: readonly string[] = ['openid', 'offline_access'];
23 >
24 > interface IResourceCacheEntry {
25 > readonly resource: string;
26 > readonly scopes: readonly string[];
27 > readonly token: IAuthorizationTokenResponse;
28 > /** Fallback identity (the IdP login account) for sessions built from this token, used when the resource token has no id_token of its own. */
29 > readonly account: vscode.AuthenticationSessionAccountInformation;
30 > readonly created_at: number;
31 > }
32 >
33 > /** Cache key for resource-scoped tokens. Exported for testing. */
34 > export function cacheKey(resource: string, scopes: readonly string[]): string {
35 return resource + '|' + [...scopes].sort().join(' ');
36 }
38 > /**
39 > * Returns true if the cached token is past (or within 60s of) its expiry. Pure
40 > * and exported for testing.
41 > *
42 > * Mints fresh ID-JAG assertions are usually short-lived (minutes). We treat tokens as expired
43 > * 60s before their nominal expiry to avoid clock skew and in-flight redemptions racing past
44 > * `exp`. Tokens without `expires_in` defined are treated as never-expiring (cached
45 > * until the process exits); `expires_in: 0` is treated as immediately expired.
46 > */
47 > export function isExpired(entry: { token: { expires_in?: number }; created_at: number }, now: number = Date.now()): boolean {
48 if (entry.token.expires_in === undefined) {
49 return false;
51 return now > entry.created_at + (entry.token.expires_in * 1000) - 60_000;
52 }
54 > /**
55 > * (Preview) Mixin that turns a {@link DynamicAuthProvider} subclass into a
56 > * Cross App Access (XAA) / enterprise-managed authentication provider, per
57 > * `draft-ietf-oauth-identity-assertion-authz-grant`.
58 > *
59 > * The IdP login leg is identical to the base class — Auth Code + PKCE against
60 > * the org-configured issuer, using the pre-registered client credentials. On
61 > * top of that:
62 > *
63 > * 1. `createSession` ensures an IdP session exists (delegated to the base
64 > * class with {@link IDP_SCOPES}).
65 > * 2. It POSTs to the IdP token endpoint with `grant_type=token-exchange`,
66 > * `subject_token=<id_token>`, `subject_token_type=id_token`,
67 > * `requested_token_type=id-jag`, `audience=<resource AS>`,
68 > * `resource=<resource indicator>`, `scope=<requested scopes>` to mint an
69 > * ID-JAG.
70 > * 3. It discovers the resource's authorization server metadata (the audience
71 > * URL) and POSTs the ID-JAG to its token endpoint with
72 > * `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`,
73 > * `assertion=<id-jag>`, `resource=<resource indicator>`,
74 > * `scope=<requested scopes>` to obtain a resource-scoped access token.
75 > * 4. The resource-scoped token is cached in-memory per `(resource, scopes)`
76 > * and returned as the session's access token.
77 > *
78 > * The resource indicator is read from `options.resource` (RFC 8707) and the
79 > * resource's authorization server URL from `options.audience` on
80 > * {@link vscode.AuthenticationProviderSessionOptions}.
81 > */
82 > export function XaaifyAuthProvider<TBase extends Ctor<DynamicAuthProvider>>(Base: TBase): TBase {
83 return class XaaAuthenticationProvider extends Base {
84 private readonly _resourceTokens = new Map<string, IResourceCacheEntry>();
365 };
366 }
368 > /**
369 > * Builds a session from a token response. Identity precedence: the token's own `id_token`, then
370 > * `fallbackAccount` (the IdP login identity), then a generic default. Never the `access_token`, which
371 > * for XAA is an opaque resource credential. Exported for testing.
372 > */
373 > export function toSession(token: IAuthorizationTokenResponse, scopes: readonly string[], fallbackAccount?: vscode.AuthenticationSessionAccountInformation): vscode.AuthenticationSession {
374 let account: vscode.AuthenticationSessionAccountInformation | undefined;
375 if (token.id_token) {
src/vs/workbench/api/common/extHostManagedSockets.ts 57 introduced LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostManagedSockets.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { ExtHostManagedSocketsShape, MainContext, MainThreadManagedSocketsShape } from './extHost.protocol.js';
7 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
8 > import * as vscode from 'vscode';
9 > import { Disposable, DisposableStore, toDisposable } from '../../../base/common/lifecycle.js';
10 > import { IExtHostRpcService } from './extHostRpcService.js';
11 > import { VSBuffer } from '../../../base/common/buffer.js';
12 >
13 > export interface IExtHostManagedSockets extends ExtHostManagedSocketsShape {
14 > setFactory(socketFactoryId: number, makeConnection: () => Thenable<vscode.ManagedMessagePassing>): void;
15 > /**
16 > * Opens a managed connection in-process using the currently registered
17 > * factory. Used by consumers that live inside the extension host (e.g. the
18 > * browser tunnel proxy). There is only ever one active remote per window, so
19 > * the latest factory is the correct one to dial; this avoids depending on a
20 > * factory id that can lag connection-data updates by a renderer round-trip.
21 > */
22 > makeConnection(): Promise<vscode.ManagedMessagePassing>;
23 > readonly _serviceBrand: undefined;
24 > }
25 >
26 > export const IExtHostManagedSockets = createDecorator<IExtHostManagedSockets>('IExtHostManagedSockets');
27 >
28 > export class ExtHostManagedSockets implements IExtHostManagedSockets {
29 > declare readonly _serviceBrand: undefined;
30 >
31 > private readonly _proxy: MainThreadManagedSocketsShape;
32 > private _remoteSocketIdCounter = 0;
33 > private _factory: ManagedSocketFactory | null = null;
34 > private readonly _managedRemoteSockets: Map<number, ManagedSocket> = new Map();
35 >
36 > constructor(
37 @IExtHostRpcService extHostRpc: IExtHostRpcService,
38 ) {
39 this._proxy = extHostRpc.getProxy(MainContext.MainThreadManagedSockets);
40 }
42 > setFactory(socketFactoryId: number, makeConnection: () => Thenable<vscode.ManagedMessagePassing>): void {
43 // Terminate all previous sockets
44 for (const socket of this._managedRemoteSockets.values()) {
54 this._proxy.$registerSocketFactory(this._factory.socketFactoryId);
55 }
57 > makeConnection(): Promise<vscode.ManagedMessagePassing> {
58 if (!this._factory) {
59 throw new Error('No managed socket factory registered');
61 return Promise.resolve(this._factory.makeConnection());
62 }
64 > async $openRemoteSocket(socketFactoryId: number): Promise<number> {
65 if (!this._factory || this._factory.socketFactoryId !== socketFactoryId) {
66 throw new Error(`No socket factory with id ${socketFactoryId}`);
85 return id;
86 }
88 > $remoteSocketWrite(socketId: number, buffer: VSBuffer): void {
89 this._managedRemoteSockets.get(socketId)?.actual.send(buffer.buffer);
90 }
92 > $remoteSocketEnd(socketId: number): void {
93 const socket = this._managedRemoteSockets.get(socketId);
94 if (socket) {
97 }
98 }
100 > async $remoteSocketDrain(socketId: number): Promise<void> {
101 await this._managedRemoteSockets.get(socketId)?.actual.drain?.();
102 }
104 >
105 > class ManagedSocketFactory {
106 > constructor(
107 public readonly socketFactoryId: number,
108 public readonly makeConnection: () => Thenable<vscode.ManagedMessagePassing>,
109 ) { }
111 >
112 > class ManagedSocket extends Disposable {
113 > constructor(
114 public readonly socketId: number,
115 public readonly actual: vscode.ManagedMessagePassing,
src/vs/workbench/api/common/extHostWindow.ts 54 introduced LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostWindow.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Emitter, Event } from '../../../base/common/event.js';
7 > import { Schemas } from '../../../base/common/network.js';
8 > import { isFalsyOrWhitespace } from '../../../base/common/strings.js';
9 > import { URI } from '../../../base/common/uri.js';
10 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
11 > import { IExtHostRpcService } from './extHostRpcService.js';
12 > import { WindowState } from 'vscode';
13 > import { ExtHostWindowShape, IOpenUriOptions, MainContext, MainThreadWindowShape } from './extHost.protocol.js';
14 > import { IExtHostInitDataService } from './extHostInitDataService.js';
15 > import { decodeBase64 } from '../../../base/common/buffer.js';
16 >
17 > export class ExtHostWindow implements ExtHostWindowShape {
18 >
19 > declare _serviceBrand: undefined;
20 >
21 > private static InitialState: WindowState = {
22 > focused: true,
23 > active: true,
24 > };
25 >
26 > private _proxy: MainThreadWindowShape;
27 >
28 > private readonly _onDidChangeWindowState = new Emitter<WindowState>();
29 > readonly onDidChangeWindowState: Event<WindowState> = this._onDidChangeWindowState.event;
30 >
31 > private _nativeHandle: Uint8Array | undefined;
32 > private _state = ExtHostWindow.InitialState;
33 >
34 > getState(): WindowState {
35 // todo@connor4312: this can be changed to just return this._state after proposed api is finalized
36 const state = this._state;
45 };
46 }
48 > constructor(
49 @IExtHostInitDataService initData: IExtHostInitDataService,
50 @IExtHostRpcService extHostRpc: IExtHostRpcService
59 });
60 }
62 > get nativeHandle(): Uint8Array | undefined {
63 return this._nativeHandle;
64 }
66 > $onDidChangeActiveNativeWindowHandle(handle: string | undefined): void {
67 this._nativeHandle = handle ? decodeBase64(handle).buffer : undefined;
68 }
70 > $onDidChangeWindowFocus(value: boolean) {
71 this.onDidChangeWindowProperty('focused', value);
72 }
74 > $onDidChangeWindowActive(value: boolean) {
75 this.onDidChangeWindowProperty('active', value);
76 }
78 > onDidChangeWindowProperty(property: keyof WindowState, value: boolean): void {
79 if (value === this._state[property]) {
80 return;
84 this._onDidChangeWindowState.fire(this._state);
85 }
87 > openUri(stringOrUri: string | URI, options: IOpenUriOptions): Promise<boolean> {
88 let uriAsString: string | undefined;
89 if (typeof stringOrUri === 'string') {
102 return this._proxy.$openUri(stringOrUri, uriAsString, options);
103 }
105 > async asExternalUri(uri: URI, options: IOpenUriOptions): Promise<URI> {
106 if (isFalsyOrWhitespace(uri.scheme)) {
107 return Promise.reject('Invalid scheme - cannot be empty');
111 return URI.from(result);
112 }
114 >
115 > export const IExtHostWindow = createDecorator<IExtHostWindow>('IExtHostWindow');
116 > export interface IExtHostWindow extends ExtHostWindow, ExtHostWindowShape { }
src/vs/workbench/api/common/extHostMemento.ts 48 introduced LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostMemento.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as vscode from 'vscode';
7 > import { IDisposable } from '../../../base/common/lifecycle.js';
8 > import { ExtHostStorage } from './extHostStorage.js';
9 > import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
10 > import { DeferredPromise, RunOnceScheduler } from '../../../base/common/async.js';
11 >
12 > export class ExtensionMemento implements vscode.Memento {
13 >
14 > protected readonly _id: string;
15 > private readonly _shared: boolean;
16 > protected readonly _storage: ExtHostStorage;
17 >
18 > private readonly _init: Promise<ExtensionMemento>;
19 > private _value?: { [n: string]: any };
20 > private readonly _storageListener: IDisposable;
21 >
22 > private _deferredPromises: Map<string, DeferredPromise<void>> = new Map();
23 > private _scheduler: RunOnceScheduler;
24 >
25 > constructor(id: string, global: boolean, storage: ExtHostStorage) {
26 this._id = id;
27 this._shared = global;
56 }, 0);
57 }
59 > keys(): readonly string[] {
60 // Filter out `undefined` values, as they can stick around in the `_value` until the `onDidChangeStorage` event runs
61 return Object.entries(this._value ?? {}).filter(([, value]) => value !== undefined).map(([key]) => key);
62 }
64 > get whenReady(): Promise<ExtensionMemento> {
65 return this._init;
66 }
68 > get<T>(key: string): T | undefined;
69 > get<T>(key: string, defaultValue: T): T;
70 > get<T>(key: string, defaultValue?: T): T {
71 let value = this._value![key];
72 if (typeof value === 'undefined') {
75 return value;
76 }
78 > update(key: string, value: any): Promise<void> {
79 if (value !== null && typeof value === 'object') {
80 // Prevent the value from being as-is for until we have
101 return promise.p;
102 }
104 > dispose(): void {
105 this._storageListener.dispose();
106 }
108 >
109 > export class ExtensionGlobalMemento extends ExtensionMemento {
110 >
111 > private readonly _extension: IExtensionDescription;
112 >
113 > setKeysForSync(keys: string[]): void {
114 this._storage.registerExtensionStorageKeysToSync({ id: this._id, version: this._extension.version }, keys);
115 }
117 > constructor(extensionDescription: IExtensionDescription, storage: ExtHostStorage) {
118 super(extensionDescription.identifier.value, true, storage);
119 this._extension = extensionDescription;
120 }
122 > }
src/vs/workbench/api/common/extHostProgress.ts 46 introduced LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostProgress.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { ProgressOptions } from 'vscode';
7 > import { MainThreadProgressShape, ExtHostProgressShape, MainContext } from './extHost.protocol.js';
8 > import { ProgressLocation } from './extHostTypeConverters.js';
9 > import { Progress, IProgressStep } from '../../../platform/progress/common/progress.js';
10 > import { CancellationTokenSource, CancellationToken } from '../../../base/common/cancellation.js';
11 > import { throttle } from '../../../base/common/decorators.js';
12 > import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
13 > import { onUnexpectedExternalError } from '../../../base/common/errors.js';
14 > import { INotificationSource } from '../../../platform/notification/common/notification.js';
15 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
16 > import { IExtHostRpcService } from './extHostRpcService.js';
17 >
18 > export interface IExtHostProgress extends ExtHostProgress { }
19 > export const IExtHostProgress = createDecorator<IExtHostProgress>('IExtHostProgress');
20 >
21 > export class ExtHostProgress implements ExtHostProgressShape {
22 >
23 > declare readonly _serviceBrand: undefined;
24 >
25 > private _proxy: MainThreadProgressShape;
26 > private _handles: number = 0;
27 > private _mapHandleToCancellationSource: Map<number, CancellationTokenSource> = new Map();
28 >
29 > constructor(@IExtHostRpcService extHostRpc: IExtHostRpcService) {
30 this._proxy = extHostRpc.getProxy(MainContext.MainThreadProgress);
31 }
33 > async withProgress<R>(extension: IExtensionDescription, options: ProgressOptions, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Promise<R> {
34 const handle = this._handles++;
35 const { title, location, cancellable } = options;
39 return this._withProgress(handle, task, !!cancellable);
40 }
42 > async withProgressFromSource<R>(source: string | INotificationSource, options: ProgressOptions, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Promise<R> {
43 const handle = this._handles++;
44 const { title, location, cancellable } = options;
47 return this._withProgress(handle, task, !!cancellable);
48 }
50 > private _withProgress<R>(handle: number, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>, cancellable: boolean): Thenable<R> {
51 let source: CancellationTokenSource | undefined;
52 if (cancellable) {
73 return p;
74 }
76 > public $acceptProgressCanceled(handle: number): void {
77 const source = this._mapHandleToCancellationSource.get(handle);
78 if (source) {
81 }
82 }
84 >
85 function mergeProgress(result: IProgressStep, currentValue: IProgressStep): IProgressStep {
86 result.message = currentValue.message;
95 return result;
96 }
98 > class ProgressCallback extends Progress<IProgressStep> {
99 > constructor(private _proxy: MainThreadProgressShape, private _handle: number) {
100 super(p => this.throttledReport(p));
101 }
103 > @throttle(100, (result: IProgressStep, currentValue: IProgressStep) => mergeProgress(result, currentValue), () => Object.create(null))
104 > throttledReport(p: IProgressStep): void {
105 this._proxy.$progressReport(this._handle, p);
106 }
src/vs/workbench/api/common/extHostFileSystemConsumer.ts 45 introduced LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostFileSystemConsumer.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { MainContext, MainThreadFileSystemShape } from './extHost.protocol.js';
7 > import type * as vscode from 'vscode';
8 > import * as files from '../../../platform/files/common/files.js';
9 > import { FileSystemError } from './extHostTypes.js';
10 > import { VSBuffer } from '../../../base/common/buffer.js';
11 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
12 > import { IExtHostRpcService } from './extHostRpcService.js';
13 > import { IExtHostFileSystemInfo } from './extHostFileSystemInfo.js';
14 > import { IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
15 > import { ResourceQueue } from '../../../base/common/async.js';
16 > import { IExtUri, extUri, extUriIgnorePathCase } from '../../../base/common/resources.js';
17 > import { Schemas } from '../../../base/common/network.js';
18 > import { IMarkdownString } from '../../../base/common/htmlContent.js';
19 >
20 > export class ExtHostConsumerFileSystem {
21 >
22 > readonly _serviceBrand: undefined;
23 >
24 > readonly value: vscode.FileSystem;
25 >
26 > private readonly _proxy: MainThreadFileSystemShape;
27 > private readonly _fileSystemProvider = new Map<string, { impl: vscode.FileSystemProvider; extUri: IExtUri; isReadonly: boolean }>();
28 >
29 > private readonly _writeQueue = new ResourceQueue();
30 >
31 > constructor(
32 @IExtHostRpcService extHostRpc: IExtHostRpcService,
33 @IExtHostFileSystemInfo fileSystemInfo: IExtHostFileSystemInfo,
158 });
159 }
161 > private async mkdirp(provider: vscode.FileSystemProvider, providerExtUri: IExtUri, directory: vscode.Uri): Promise<void> {
162 const directoriesToCreate: string[] = [];
163
201 }
202 }
204 > private static _handleError(err: any): never {
205 // desired error type
206 if (err instanceof FileSystemError) {
244 }
245 }
247 > // ---
248 >
249 > addFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options?: { isCaseSensitive?: boolean; isReadonly?: boolean | IMarkdownString }): IDisposable {
250 this._fileSystemProvider.set(scheme, { impl: provider, extUri: options?.isCaseSensitive ? extUri : extUriIgnorePathCase, isReadonly: !!options?.isReadonly });
251 return toDisposable(() => this._fileSystemProvider.delete(scheme));
252 }
254 > getFileSystemProviderExtUri(scheme: string) {
255 return this._fileSystemProvider.get(scheme)?.extUri ?? extUri;
256 }
258 >
259 > export interface IExtHostConsumerFileSystem extends ExtHostConsumerFileSystem { }
260 > export const IExtHostConsumerFileSystem = createDecorator<IExtHostConsumerFileSystem>('IExtHostConsumerFileSystem');
src/vs/workbench/api/common/extHostStoragePaths.ts 45 introduced LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostStoragePaths.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
7 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
8 > import { IExtHostInitDataService } from './extHostInitDataService.js';
9 > import { ILogService } from '../../../platform/log/common/log.js';
10 > import { IEnvironment, IStaticWorkspaceData } from '../../services/extensions/common/extensionHostProtocol.js';
11 > import { IExtHostConsumerFileSystem } from './extHostFileSystemConsumer.js';
12 > import { URI } from '../../../base/common/uri.js';
13 >
14 > export const IExtensionStoragePaths = createDecorator<IExtensionStoragePaths>('IExtensionStoragePaths');
15 >
16 > export interface IExtensionStoragePaths {
17 > readonly _serviceBrand: undefined;
18 > whenReady: Promise<any>;
19 > workspaceValue(extension: IExtensionDescription): URI | undefined;
20 > globalValue(extension: IExtensionDescription): URI;
21 > onWillDeactivateAll(): void;
22 > }
23 >
24 > export class ExtensionStoragePaths implements IExtensionStoragePaths {
25 >
26 > readonly _serviceBrand: undefined;
27 >
28 > private readonly _workspace?: IStaticWorkspaceData;
29 > protected readonly _environment: IEnvironment;
30 >
31 > readonly whenReady: Promise<URI | undefined>;
32 > private _value?: URI;
33 >
34 > constructor(
35 @IExtHostInitDataService initData: IExtHostInitDataService,
36 @ILogService protected readonly _logService: ILogService,
41 this.whenReady = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
42 }
44 > protected async _getWorkspaceStorageURI(storageName: string): Promise<URI> {
45 return URI.joinPath(this._environment.workspaceStorageHome, storageName);
46 }
48 > private async _getOrCreateWorkspaceStoragePath(): Promise<URI | undefined> {
49 if (!this._workspace) {
50 return Promise.resolve(undefined);
79 }
80 }
82 > workspaceValue(extension: IExtensionDescription): URI | undefined {
83 if (this._value) {
84 return URI.joinPath(this._value, extension.identifier.value);
src/vs/workbench/api/common/extHostStorage.ts 42 introduced LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostStorage.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { MainContext, MainThreadStorageShape, ExtHostStorageShape } from './extHost.protocol.js';
7 > import { Emitter } from '../../../base/common/event.js';
8 > import { IExtHostRpcService } from './extHostRpcService.js';
9 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
10 > import { IExtensionIdWithVersion } from '../../../platform/extensionManagement/common/extensionStorage.js';
11 > import { ILogService } from '../../../platform/log/common/log.js';
12 >
13 > export interface IStorageChangeEvent {
14 > shared: boolean;
15 > key: string;
16 > value: object;
17 > }
18 >
19 > export class ExtHostStorage implements ExtHostStorageShape {
20 >
21 > readonly _serviceBrand: undefined;
22 >
23 > private _proxy: MainThreadStorageShape;
24 >
25 > private readonly _onDidChangeStorage = new Emitter<IStorageChangeEvent>();
26 > readonly onDidChangeStorage = this._onDidChangeStorage.event;
27 >
28 > constructor(
29 mainContext: IExtHostRpcService,
30 private readonly _logService: ILogService
32 this._proxy = mainContext.getProxy(MainContext.MainThreadStorage);
33 }
35 > registerExtensionStorageKeysToSync(extension: IExtensionIdWithVersion, keys: string[]): void {
36 this._proxy.$registerExtensionStorageKeysToSync(extension, keys);
37 }
39 > async initializeExtensionStorage(shared: boolean, key: string, defaultValue?: object): Promise<object | undefined> {
40 const value = await this._proxy.$initializeExtensionStorage(shared, key);
41
47 return parsedValue || defaultValue;
48 }
50 > setValue(shared: boolean, key: string, value: object): Promise<void> {
51 return this._proxy.$setValue(shared, key, value);
52 }
54 > $acceptValue(shared: boolean, key: string, value: string): void {
55 const parsedValue = this.safeParseValue(shared, key, value);
56 if (parsedValue) {
58 }
59 }
61 > private safeParseValue(shared: boolean, key: string, value: string): object | undefined {
62 try {
63 return JSON.parse(value);
70 return undefined;
71 }
73 >
74 > export interface IExtHostStorage extends ExtHostStorage { }
75 > export const IExtHostStorage = createDecorator<IExtHostStorage>('IExtHostStorage');
src/vs/workbench/api/common/extHostLocalizationService.ts 39 introduced LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostLocalizationService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { LANGUAGE_DEFAULT } from '../../../base/common/platform.js';
7 > import { format2 } from '../../../base/common/strings.js';
8 > import { URI } from '../../../base/common/uri.js';
9 > import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
10 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
11 > import { ILogService } from '../../../platform/log/common/log.js';
12 > import { ExtHostLocalizationShape, IStringDetails, MainContext, MainThreadLocalizationShape } from './extHost.protocol.js';
13 > import { IExtHostInitDataService } from './extHostInitDataService.js';
14 > import { IExtHostRpcService } from './extHostRpcService.js';
15 >
16 > export class ExtHostLocalizationService implements ExtHostLocalizationShape {
17 > readonly _serviceBrand: undefined;
18 >
19 > private readonly _proxy: MainThreadLocalizationShape;
20 > private readonly currentLanguage: string;
21 > private readonly isDefaultLanguage: boolean;
22 >
23 > private readonly bundleCache: Map<string, { contents: { [key: string]: string }; uri: URI }> = new Map();
24 >
25 > constructor(
26 @IExtHostInitDataService initData: IExtHostInitDataService,
27 @IExtHostRpcService rpc: IExtHostRpcService,
32 this.isDefaultLanguage = this.currentLanguage === LANGUAGE_DEFAULT;
33 }
35 > getMessage(extensionId: string, details: IStringDetails): string {
36 const { message, args, comment } = details;
37 if (this.isDefaultLanguage) {
49 return format2(str ?? message, (args ?? {}));
50 }
52 > getBundle(extensionId: string): { [key: string]: string } | undefined {
53 return this.bundleCache.get(extensionId)?.contents;
54 }
56 > getBundleUri(extensionId: string): URI | undefined {
57 return this.bundleCache.get(extensionId)?.uri;
58 }
60 > async initializeLocalizedMessages(extension: IExtensionDescription): Promise<void> {
61 if (this.isDefaultLanguage
62 || (!extension.l10n && !extension.isBuiltin)
93 }
94 }
96 > private async getBundleLocation(extension: IExtensionDescription): Promise<URI | undefined> {
97 if (extension.isBuiltin) {
98 const uri = await this._proxy.$fetchBuiltInBundleUri(extension.identifier.value, this.currentLanguage);
104 : undefined;
105 }
107 >
108 > export const IExtHostLocalizationService = createDecorator<IExtHostLocalizationService>('IExtHostLocalizationService');
109 > export interface IExtHostLocalizationService extends ExtHostLocalizationService { }
src/vs/workbench/services/extensions/common/workspaceContains.ts 37 introduced LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- workspaceContains.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import * as resources from '../../../../base/common/resources.js';
7 > import { URI, UriComponents } from '../../../../base/common/uri.js';
8 > import { CancellationTokenSource, CancellationToken } from '../../../../base/common/cancellation.js';
9 > import * as errors from '../../../../base/common/errors.js';
10 > import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
11 > import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
12 > import { QueryBuilder } from '../../search/common/queryBuilder.js';
13 > import { ISearchService } from '../../search/common/search.js';
14 > import { toWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js';
15 > import { ILogService } from '../../../../platform/log/common/log.js';
16 > import { promiseWithResolvers } from '../../../../base/common/async.js';
17 >
18 > const WORKSPACE_CONTAINS_TIMEOUT = 7000;
19 >
20 > export interface IExtensionActivationHost {
21 > readonly logService: ILogService;
22 > readonly folders: readonly UriComponents[];
23 > readonly forceUsingSearch: boolean;
24 >
25 > exists(uri: URI): Promise<boolean>;
26 > checkExists(folders: readonly UriComponents[], includes: string[], token: CancellationToken): Promise<boolean>;
27 > }
28 >
29 > export interface IExtensionActivationResult {
30 > activationEvent: string;
31 > }
32 >
33 > export function checkActivateWorkspaceContainsExtension(host: IExtensionActivationHost, desc: IExtensionDescription): Promise<IExtensionActivationResult | undefined> {
34 const activationEvents = desc.activationEvents;
35 if (!activationEvents) {
68 return promise;
69 }
71 async function _activateIfFileName(host: IExtensionActivationHost, fileName: string, activate: (activationEvent: string) => void): Promise<void> {
72 // find exact path
79 }
80 }
82 async function _activateIfGlobPatterns(host: IExtensionActivationHost, extensionId: ExtensionIdentifier, globPatterns: string[], activate: (activationEvent: string) => void): Promise<void> {
83 if (globPatterns.length === 0) {
110 }
111 }
113 > export function checkGlobFileExists(
114 accessor: ServicesAccessor,
115 folders: readonly UriComponents[],
src/vs/workbench/api/common/extHostUrls.ts 35 introduced LOC · 5 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostUrls.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as vscode from 'vscode';
7 > import { MainContext, ExtHostUrlsShape, MainThreadUrlsShape } from './extHost.protocol.js';
8 > import { URI, UriComponents } from '../../../base/common/uri.js';
9 > import { toDisposable } from '../../../base/common/lifecycle.js';
10 > import { onUnexpectedError } from '../../../base/common/errors.js';
11 > import { ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
12 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
13 > import { IExtHostRpcService } from './extHostRpcService.js';
14 >
15 > export class ExtHostUrls implements ExtHostUrlsShape {
16 >
17 > declare _serviceBrand: undefined;
18 >
19 > private static HandlePool = 0;
20 > private readonly _proxy: MainThreadUrlsShape;
21 >
22 > private handles = new ExtensionIdentifierSet();
23 > private handlers = new Map<number, vscode.UriHandler>();
24 >
25 > constructor(
26 @IExtHostRpcService extHostRpc: IExtHostRpcService
27 ) {
28 this._proxy = extHostRpc.getProxy(MainContext.MainThreadUrls);
29 }
31 > registerUriHandler(extension: IExtensionDescription, handler: vscode.UriHandler): vscode.Disposable {
32 const extensionId = extension.identifier;
33 if (this.handles.has(extensionId)) {
46 });
47 }
49 > $handleExternalUri(handle: number, uri: UriComponents): Promise<void> {
50 const handler = this.handlers.get(handle);
51
61 return Promise.resolve(undefined);
62 }
64 > async createAppUri(uri: URI): Promise<vscode.Uri> {
65 return URI.revive(await this._proxy.$createAppUri(uri));
66 }
68 >
69 > export interface IExtHostUrlsService extends ExtHostUrls { }
70 > export const IExtHostUrlsService = createDecorator<IExtHostUrlsService>('IExtHostUrlsService');
src/vs/workbench/api/common/extHostSecrets.ts 32 introduced LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostSecrets.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as vscode from 'vscode';
7 >
8 > import { ExtHostSecretState } from './extHostSecretState.js';
9 > import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
10 > import { Event } from '../../../base/common/event.js';
11 > import { DisposableStore } from '../../../base/common/lifecycle.js';
12 >
13 > export class ExtensionSecrets implements vscode.SecretStorage {
14 >
15 > protected readonly _id: string;
16 > readonly #secretState: ExtHostSecretState;
17 >
18 > readonly onDidChange: Event<vscode.SecretStorageChangeEvent>;
19 > readonly disposables = new DisposableStore();
20 >
21 > constructor(extensionDescription: IExtensionDescription, secretState: ExtHostSecretState) {
22 this._id = ExtensionIdentifier.toKey(extensionDescription.identifier);
23 this.#secretState = secretState;
29 );
30 }
32 > dispose() {
33 this.disposables.dispose();
34 }
36 > get(key: string): Promise<string | undefined> {
37 return this.#secretState.get(this._id, key);
38 }
40 > store(key: string, value: string): Promise<void> {
41 return this.#secretState.store(this._id, key, value);
42 }
44 > delete(key: string): Promise<void> {
45 return this.#secretState.delete(this._id, key);
46 }
48 > keys(): Promise<string[]> {
49 return this.#secretState.keys(this._id) || [];
50 }
src/vs/workbench/api/common/extHostSecretState.ts 30 introduced LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostSecretState.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { ExtHostSecretStateShape, MainContext, MainThreadSecretStateShape } from './extHost.protocol.js';
7 > import { Emitter } from '../../../base/common/event.js';
8 > import { IExtHostRpcService } from './extHostRpcService.js';
9 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
10 >
11 > export class ExtHostSecretState implements ExtHostSecretStateShape {
12 > private _proxy: MainThreadSecretStateShape;
13 > private _onDidChangePassword = new Emitter<{ extensionId: string; key: string }>();
14 > readonly onDidChangePassword = this._onDidChangePassword.event;
15 >
16 > constructor(mainContext: IExtHostRpcService) {
17 this._proxy = mainContext.getProxy(MainContext.MainThreadSecretState);
18 }
20 > async $onDidChangePassword(e: { extensionId: string; key: string }): Promise<void> {
21 this._onDidChangePassword.fire(e);
22 }
24 > get(extensionId: string, key: string): Promise<string | undefined> {
25 return this._proxy.$getPassword(extensionId, key);
26 }
28 > store(extensionId: string, key: string, value: string): Promise<void> {
29 return this._proxy.$setPassword(extensionId, key, value);
30 }
32 > delete(extensionId: string, key: string): Promise<void> {
33 return this._proxy.$deletePassword(extensionId, key);
34 }
36 > keys(extensionId: string): Promise<string[]> {
37 return this._proxy.$getKeys(extensionId);
38 }
40 >
41 > export interface IExtHostSecretState extends ExtHostSecretState { }
42 > export const IExtHostSecretState = createDecorator<IExtHostSecretState>('IExtHostSecretState');