@theia/plugin-ext 1.74.0-next.5 → 1.74.0-next.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
- package/lib/hosted/browser/hosted-plugin.js +14 -3
- package/lib/hosted/browser/hosted-plugin.js.map +1 -1
- package/lib/hosted/node/plugin-reader.d.ts +6 -5
- package/lib/hosted/node/plugin-reader.d.ts.map +1 -1
- package/lib/hosted/node/plugin-reader.js +14 -7
- package/lib/hosted/node/plugin-reader.js.map +1 -1
- package/lib/hosted/node/plugin-reader.spec.d.ts +2 -0
- package/lib/hosted/node/plugin-reader.spec.d.ts.map +1 -0
- package/lib/hosted/node/plugin-reader.spec.js +72 -0
- package/lib/hosted/node/plugin-reader.spec.js.map +1 -0
- package/lib/main/browser/custom-editors/custom-editor-opener.d.ts.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-opener.js +25 -1
- package/lib/main/browser/custom-editors/custom-editor-opener.js.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-opener.spec.d.ts +2 -0
- package/lib/main/browser/custom-editors/custom-editor-opener.spec.d.ts.map +1 -0
- package/lib/main/browser/custom-editors/custom-editor-opener.spec.js +90 -0
- package/lib/main/browser/custom-editors/custom-editor-opener.spec.js.map +1 -0
- package/lib/main/browser/custom-editors/custom-editor-widget.d.ts +4 -1
- package/lib/main/browser/custom-editors/custom-editor-widget.d.ts.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-widget.js +14 -0
- package/lib/main/browser/custom-editors/custom-editor-widget.js.map +1 -1
- package/lib/main/browser/preference-registry-main.d.ts.map +1 -1
- package/lib/main/browser/preference-registry-main.js +8 -0
- package/lib/main/browser/preference-registry-main.js.map +1 -1
- package/lib/main/node/plugin-http-resolver.d.ts.map +1 -1
- package/lib/main/node/plugin-http-resolver.js +5 -3
- package/lib/main/node/plugin-http-resolver.js.map +1 -1
- package/lib/main/node/plugin-http-resolver.spec.d.ts +2 -0
- package/lib/main/node/plugin-http-resolver.spec.d.ts.map +1 -0
- package/lib/main/node/plugin-http-resolver.spec.js +40 -0
- package/lib/main/node/plugin-http-resolver.spec.js.map +1 -0
- package/lib/main/node/plugin-service.d.ts.map +1 -1
- package/lib/main/node/plugin-service.js +8 -3
- package/lib/main/node/plugin-service.js.map +1 -1
- package/lib/plugin/telemetry-ext.d.ts +4 -1
- package/lib/plugin/telemetry-ext.d.ts.map +1 -1
- package/lib/plugin/telemetry-ext.js +19 -3
- package/lib/plugin/telemetry-ext.js.map +1 -1
- package/lib/plugin/telemetry-ext.spec.d.ts +2 -0
- package/lib/plugin/telemetry-ext.spec.d.ts.map +1 -0
- package/lib/plugin/telemetry-ext.spec.js +51 -0
- package/lib/plugin/telemetry-ext.spec.js.map +1 -0
- package/package.json +31 -31
- package/src/hosted/browser/hosted-plugin.ts +14 -3
- package/src/hosted/node/plugin-reader.spec.ts +80 -0
- package/src/hosted/node/plugin-reader.ts +16 -7
- package/src/main/browser/custom-editors/custom-editor-opener.spec.ts +104 -0
- package/src/main/browser/custom-editors/custom-editor-opener.tsx +26 -1
- package/src/main/browser/custom-editors/custom-editor-widget.ts +15 -1
- package/src/main/browser/preference-registry-main.ts +8 -0
- package/src/main/node/plugin-http-resolver.spec.ts +41 -0
- package/src/main/node/plugin-http-resolver.ts +4 -3
- package/src/main/node/plugin-service.ts +7 -3
- package/src/plugin/telemetry-ext.spec.ts +61 -0
- package/src/plugin/telemetry-ext.ts +23 -4
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 Safi Seid-Ahmad, K2view and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
|
18
|
+
let disableJSDOM = enableJSDOM();
|
|
19
|
+
|
|
20
|
+
import * as chai from 'chai';
|
|
21
|
+
import URI from '@theia/core/lib/common/uri';
|
|
22
|
+
import { CustomEditorOpener } from './custom-editor-opener';
|
|
23
|
+
import { CustomEditor, CustomEditorPriority } from '../../../common';
|
|
24
|
+
|
|
25
|
+
disableJSDOM();
|
|
26
|
+
|
|
27
|
+
const expect = chai.expect;
|
|
28
|
+
|
|
29
|
+
describe('CustomEditorOpener#selectorMatches', () => {
|
|
30
|
+
|
|
31
|
+
before(() => {
|
|
32
|
+
disableJSDOM = enableJSDOM();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
after(() => {
|
|
36
|
+
disableJSDOM();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Only selector matching is exercised — none of the collaborators are touched.
|
|
40
|
+
function createOpener(filenamePattern: string): CustomEditorOpener {
|
|
41
|
+
const editor: CustomEditor = {
|
|
42
|
+
viewType: 'test.editor',
|
|
43
|
+
displayName: 'Test Editor',
|
|
44
|
+
selector: [{ filenamePattern }],
|
|
45
|
+
priority: CustomEditorPriority.default
|
|
46
|
+
};
|
|
47
|
+
return new CustomEditorOpener(editor, undefined!, undefined!, undefined!, undefined!);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function matches(filenamePattern: string, path: string): boolean {
|
|
51
|
+
const opener = createOpener(filenamePattern);
|
|
52
|
+
return opener.matches([{ filenamePattern }], new URI(`file://${path}`));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function matchesUri(filenamePattern: string, uri: string): boolean {
|
|
56
|
+
const opener = createOpener(filenamePattern);
|
|
57
|
+
return opener.matches([{ filenamePattern }], new URI(uri));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
it('matches a plain extension pattern against the basename', () => {
|
|
61
|
+
expect(matches('*.custom', '/project/src/file.custom')).true;
|
|
62
|
+
expect(matches('*.custom', '/project/src/file.other')).false;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('matches a plain filename pattern against the basename anywhere', () => {
|
|
66
|
+
expect(matches('config.json', '/anywhere/at/all/config.json')).true;
|
|
67
|
+
expect(matches('config.json', '/anywhere/at/all/other.json')).false;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('matches case-insensitively', () => {
|
|
71
|
+
expect(matches('*.CUSTOM', '/project/file.custom')).true;
|
|
72
|
+
expect(matches('*.custom', '/project/FILE.CUSTOM')).true;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('matches a pattern containing a path separator against the full path (VS Code parity)', () => {
|
|
76
|
+
const pattern = '**/components/*/config.json';
|
|
77
|
+
expect(matches(pattern, '/project/src/components/button/config.json')).true;
|
|
78
|
+
expect(matches(pattern, '/project/elsewhere/config.json')).false;
|
|
79
|
+
expect(matches(pattern, '/project/src/components/button/nested/config.json')).false;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('matches nested path patterns', () => {
|
|
83
|
+
const pattern = '**/components/*/styles/*.css';
|
|
84
|
+
expect(matches(pattern, '/project/src/components/button/styles/theme.css')).true;
|
|
85
|
+
expect(matches(pattern, '/project/src/components/button/config.json')).false;
|
|
86
|
+
expect(matches(pattern, '/project/src/components/button/styles/dark/theme.css')).false;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('does not let a path pattern match a bare basename', () => {
|
|
90
|
+
expect(matches('**/components/*/config.json', '/config.json')).false;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('never matches the excluded schemes, mirroring VS Code (globMatchesResource)', () => {
|
|
94
|
+
// Patterns that would match the basename or full path on a `file:` resource...
|
|
95
|
+
expect(matchesUri('*.json', 'file:///project/settings.json')).true;
|
|
96
|
+
expect(matchesUri('**/settings.json', 'file:///project/settings.json')).true;
|
|
97
|
+
// ...must not match on VS Code's internal schemes.
|
|
98
|
+
expect(matchesUri('*.json', 'vscode-settings:/settings.json')).false;
|
|
99
|
+
expect(matchesUri('**/settings.json', 'vscode-settings:/project/settings.json')).false;
|
|
100
|
+
expect(matchesUri('*.json', 'webview-panel:/panel/config.json')).false;
|
|
101
|
+
expect(matchesUri('*', 'extension:/some/resource')).false;
|
|
102
|
+
expect(matchesUri('*', 'vscode-workspace-trust:/trust')).false;
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -24,6 +24,20 @@ import { PluginCustomEditorRegistry } from './plugin-custom-editor-registry';
|
|
|
24
24
|
import { generateUuid } from '@theia/core/lib/common/uuid';
|
|
25
25
|
import { DisposableCollection, Emitter, PreferenceService } from '@theia/core';
|
|
26
26
|
import { match } from '@theia/core/lib/common/glob';
|
|
27
|
+
import { Schemes } from '../../../common/uri-components';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Schemes that never match a custom editor's `filenamePattern`, mirroring the
|
|
31
|
+
* excluded schemes in VS Code's `editorResolverService#globMatchesResource`.
|
|
32
|
+
* Theia has no scheme constants for `extension` and `vscode-workspace-trust`,
|
|
33
|
+
* so those are referenced by their literal values.
|
|
34
|
+
*/
|
|
35
|
+
const nonMatchingSchemes = new Set<string>([
|
|
36
|
+
'extension',
|
|
37
|
+
Schemes.webviewPanel,
|
|
38
|
+
'vscode-workspace-trust',
|
|
39
|
+
Schemes.vscodeSettings
|
|
40
|
+
]);
|
|
27
41
|
|
|
28
42
|
export class CustomEditorOpener implements OpenHandler {
|
|
29
43
|
|
|
@@ -200,8 +214,19 @@ export class CustomEditorOpener implements OpenHandler {
|
|
|
200
214
|
}
|
|
201
215
|
|
|
202
216
|
selectorMatches(selector: CustomEditorSelector, resource: URI): boolean {
|
|
217
|
+
if (nonMatchingSchemes.has(resource.scheme)) {
|
|
218
|
+
// These schemes match no glob pattern, mirroring VS Code's `globMatchesResource`.
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
203
221
|
if (selector.filenamePattern) {
|
|
204
|
-
|
|
222
|
+
const filenamePattern = selector.filenamePattern.toLowerCase();
|
|
223
|
+
// Mirror VS Code's editor-association matching (editorResolverService#globMatchesResource):
|
|
224
|
+
// a pattern containing a path separator is matched against the full `scheme:path`,
|
|
225
|
+
// a plain pattern is matched against the basename only.
|
|
226
|
+
const target = filenamePattern.includes('/')
|
|
227
|
+
? `${resource.scheme}:${resource.path.toString()}`.toLowerCase()
|
|
228
|
+
: resource.path.base.toLowerCase();
|
|
229
|
+
if (match(filenamePattern, target)) {
|
|
205
230
|
return true;
|
|
206
231
|
}
|
|
207
232
|
}
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
|
|
18
18
|
import URI from '@theia/core/lib/common/uri';
|
|
19
19
|
import { FileOperation } from '@theia/filesystem/lib/common/files';
|
|
20
|
-
import { ApplicationShell, DelegatingSaveable, NavigatableWidget, Saveable, SaveableSource } from '@theia/core/lib/browser';
|
|
20
|
+
import { ApplicationShell, DelegatingSaveable, Message, NavigatableWidget, Saveable, SaveableSource } from '@theia/core/lib/browser';
|
|
21
21
|
import { SaveableService } from '@theia/core/lib/browser/saveable-service';
|
|
22
|
+
import { Disposable, SelectionService } from '@theia/core/lib/common';
|
|
22
23
|
import { Reference } from '@theia/core/lib/common/reference';
|
|
23
24
|
import { WebviewWidget } from '../webview/webview';
|
|
24
25
|
import { CustomEditorModel } from './custom-editors-main';
|
|
@@ -54,6 +55,9 @@ export class CustomEditorWidget extends WebviewWidget implements CustomEditorWid
|
|
|
54
55
|
@inject(SaveableService)
|
|
55
56
|
protected readonly saveService: SaveableService;
|
|
56
57
|
|
|
58
|
+
@inject(SelectionService)
|
|
59
|
+
protected readonly selectionService: SelectionService;
|
|
60
|
+
|
|
57
61
|
@postConstruct()
|
|
58
62
|
protected override init(): void {
|
|
59
63
|
super.init();
|
|
@@ -63,6 +67,16 @@ export class CustomEditorWidget extends WebviewWidget implements CustomEditorWid
|
|
|
63
67
|
this.doMove(e.target.resource);
|
|
64
68
|
}
|
|
65
69
|
}));
|
|
70
|
+
this.toDispose.push(Disposable.create(() => {
|
|
71
|
+
if (this.selectionService.selection === this) {
|
|
72
|
+
this.selectionService.selection = undefined;
|
|
73
|
+
}
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
protected override onActivateRequest(msg: Message): void {
|
|
78
|
+
super.onActivateRequest(msg);
|
|
79
|
+
this.selectionService.selection = this;
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
updateID(): void {
|
|
@@ -36,8 +36,16 @@ import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposa
|
|
|
36
36
|
|
|
37
37
|
export function getPreferences(preferenceProviderProvider: PreferenceProviderProvider, rootFolders: FileStat[]): PreferenceData {
|
|
38
38
|
const folders = rootFolders.map(root => root.resource.toString());
|
|
39
|
+
// Session-scoped values are process-lifetime CLI overrides (see PreferenceScope.Session).
|
|
40
|
+
// The plugin-side `parse()` in `preference-registry.ts` only reads up to Folder, so
|
|
41
|
+
// shipping session data across would leak potentially security-sensitive overrides
|
|
42
|
+
// (e.g. AI tool auto-approval) into the plugin host without any consumer on the other
|
|
43
|
+
// side. Exclude the scope explicitly until plugins can opt in to session values.
|
|
39
44
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
45
|
return PreferenceScope.getScopes().reduce((result: { [key: number]: any }, scope: PreferenceScope) => {
|
|
46
|
+
if (scope === PreferenceScope.Session) {
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
41
49
|
result[scope] = {};
|
|
42
50
|
const provider = preferenceProviderProvider(scope);
|
|
43
51
|
if (provider) {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import { HttpPluginDeployerResolver } from './plugin-http-resolver';
|
|
19
|
+
import { PluginDeployerResolverContext } from '../../common';
|
|
20
|
+
|
|
21
|
+
describe('HttpPluginDeployerResolver', () => {
|
|
22
|
+
|
|
23
|
+
function createContext(originId: string): PluginDeployerResolverContext {
|
|
24
|
+
return {
|
|
25
|
+
getOriginId: () => originId
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
} as any as PluginDeployerResolverContext;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
it('should reject a malformed link URI', async () => {
|
|
31
|
+
const resolver = new HttpPluginDeployerResolver();
|
|
32
|
+
let error: Error | undefined;
|
|
33
|
+
try {
|
|
34
|
+
await resolver.resolve(createContext(':::not a url'));
|
|
35
|
+
} catch (e) {
|
|
36
|
+
error = e as Error;
|
|
37
|
+
}
|
|
38
|
+
expect(error).to.be.an.instanceOf(Error);
|
|
39
|
+
expect(error!.message).to.contain('invalid link URI');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -19,7 +19,6 @@ import { inject, injectable } from '@theia/core/shared/inversify';
|
|
|
19
19
|
import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
20
20
|
import { promises as fs } from 'fs';
|
|
21
21
|
import * as path from 'path';
|
|
22
|
-
import * as url from 'url';
|
|
23
22
|
import { PluginDeployerResolver, PluginDeployerResolverContext } from '../../common';
|
|
24
23
|
import { getTempDirPathAsync } from './temp-dir-util';
|
|
25
24
|
|
|
@@ -56,8 +55,10 @@ export class HttpPluginDeployerResolver implements PluginDeployerResolver {
|
|
|
56
55
|
// download the file
|
|
57
56
|
// keep filename of the url
|
|
58
57
|
const urlPath = pluginResolverContext.getOriginId();
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
let link: URL;
|
|
59
|
+
try {
|
|
60
|
+
link = new URL(urlPath);
|
|
61
|
+
} catch {
|
|
61
62
|
throw new Error('invalid link URI' + urlPath);
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
|
|
17
17
|
import * as http from 'http';
|
|
18
18
|
import * as path from 'path';
|
|
19
|
-
import * as url from 'url';
|
|
20
19
|
const vhost = require('vhost');
|
|
21
20
|
import * as express from '@theia/core/shared/express';
|
|
22
21
|
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
|
|
@@ -63,8 +62,13 @@ export class PluginApiContribution implements BackendApplicationContribution, Ws
|
|
|
63
62
|
|
|
64
63
|
allowWsUpgrade(request: http.IncomingMessage): MaybePromise<boolean> {
|
|
65
64
|
if (request.headers.origin && !this.serveSameOrigin) {
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
let host: string | undefined;
|
|
66
|
+
try {
|
|
67
|
+
host = new URL(request.headers.origin).host;
|
|
68
|
+
} catch {
|
|
69
|
+
host = undefined;
|
|
70
|
+
}
|
|
71
|
+
if (host && this.webviewExternalEndpointRegExp.test(host)) {
|
|
68
72
|
// If the origin comes from the WebViews, refuse:
|
|
69
73
|
return false;
|
|
70
74
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 suzunn and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import * as chai from 'chai';
|
|
18
|
+
import { TelemetryExtImpl } from './telemetry-ext';
|
|
19
|
+
|
|
20
|
+
const expect = chai.expect;
|
|
21
|
+
|
|
22
|
+
describe('TelemetryLogger', () => {
|
|
23
|
+
const sender = {
|
|
24
|
+
sendEventData: () => { },
|
|
25
|
+
sendErrorData: () => { }
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
it('reflects the current telemetry enabled state', () => {
|
|
29
|
+
const telemetry = new TelemetryExtImpl();
|
|
30
|
+
const logger = telemetry.createTelemetryLogger(sender);
|
|
31
|
+
|
|
32
|
+
expect(logger.telemetryEnabled).to.equal(false);
|
|
33
|
+
expect(logger.isUsageEnabled).to.equal(false);
|
|
34
|
+
expect(logger.isErrorsEnabled).to.equal(false);
|
|
35
|
+
|
|
36
|
+
telemetry.isTelemetryEnabled = true;
|
|
37
|
+
|
|
38
|
+
expect(logger.telemetryEnabled).to.equal(true);
|
|
39
|
+
expect(logger.isUsageEnabled).to.equal(true);
|
|
40
|
+
expect(logger.isErrorsEnabled).to.equal(true);
|
|
41
|
+
|
|
42
|
+
telemetry.isTelemetryEnabled = false;
|
|
43
|
+
|
|
44
|
+
expect(logger.telemetryEnabled).to.equal(false);
|
|
45
|
+
expect(logger.isUsageEnabled).to.equal(false);
|
|
46
|
+
expect(logger.isErrorsEnabled).to.equal(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('emits enable-state changes when telemetry is toggled', () => {
|
|
50
|
+
const telemetry = new TelemetryExtImpl();
|
|
51
|
+
const logger = telemetry.createTelemetryLogger(sender);
|
|
52
|
+
let changes = 0;
|
|
53
|
+
|
|
54
|
+
logger.onDidChangeEnableStates(() => changes++);
|
|
55
|
+
|
|
56
|
+
telemetry.isTelemetryEnabled = true;
|
|
57
|
+
telemetry.isTelemetryEnabled = false;
|
|
58
|
+
|
|
59
|
+
expect(changes).to.equal(2);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -49,7 +49,7 @@ export class TelemetryLogger {
|
|
|
49
49
|
readonly options: TelemetryLoggerOptions | undefined;
|
|
50
50
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
51
|
readonly commonProperties: Record<string, any>;
|
|
52
|
-
|
|
52
|
+
private _telemetryEnabled: boolean;
|
|
53
53
|
|
|
54
54
|
private readonly onDidChangeEnableStatesEmitter: Emitter<TelemetryLogger> = new Emitter();
|
|
55
55
|
readonly onDidChangeEnableStates: Event<TelemetryLogger> = this.onDidChangeEnableStatesEmitter.event;
|
|
@@ -60,9 +60,20 @@ export class TelemetryLogger {
|
|
|
60
60
|
this.sender = sender;
|
|
61
61
|
this.options = options;
|
|
62
62
|
this.commonProperties = this.getCommonProperties();
|
|
63
|
-
this.
|
|
64
|
-
this.
|
|
65
|
-
this.
|
|
63
|
+
this._telemetryEnabled = telemetryEnabled;
|
|
64
|
+
this._isErrorsEnabled = telemetryEnabled;
|
|
65
|
+
this._isUsageEnabled = telemetryEnabled;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get telemetryEnabled(): boolean {
|
|
69
|
+
return this._telemetryEnabled;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
set telemetryEnabled(telemetryEnabled: boolean) {
|
|
73
|
+
if (this._telemetryEnabled !== telemetryEnabled) {
|
|
74
|
+
this._telemetryEnabled = telemetryEnabled;
|
|
75
|
+
this.updateEnableStates(telemetryEnabled, telemetryEnabled);
|
|
76
|
+
}
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
get isUsageEnabled(): boolean {
|
|
@@ -87,6 +98,14 @@ export class TelemetryLogger {
|
|
|
87
98
|
}
|
|
88
99
|
}
|
|
89
100
|
|
|
101
|
+
private updateEnableStates(isUsageEnabled: boolean, isErrorsEnabled: boolean): void {
|
|
102
|
+
if (this._isUsageEnabled !== isUsageEnabled || this._isErrorsEnabled !== isErrorsEnabled) {
|
|
103
|
+
this._isUsageEnabled = isUsageEnabled;
|
|
104
|
+
this._isErrorsEnabled = isErrorsEnabled;
|
|
105
|
+
this.onDidChangeEnableStatesEmitter.fire(this);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
90
109
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
110
|
logUsage(eventName: string, data?: Record<string, any | TelemetryTrustedValue<any>>): void {
|
|
92
111
|
if (!this.telemetryEnabled || !this.isUsageEnabled) {
|