@theia/plugin-ext 1.76.0-next.6 → 1.76.0

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.
Files changed (221) hide show
  1. package/lib/common/paths-util.d.ts.map +1 -1
  2. package/lib/common/paths-util.js +9 -2
  3. package/lib/common/paths-util.js.map +1 -1
  4. package/lib/common/paths-util.spec.d.ts +2 -0
  5. package/lib/common/paths-util.spec.d.ts.map +1 -0
  6. package/lib/common/paths-util.spec.js +42 -0
  7. package/lib/common/paths-util.spec.js.map +1 -0
  8. package/lib/common/plugin-api-rpc.d.ts +14 -0
  9. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  10. package/lib/common/plugin-api-rpc.js +1 -1
  11. package/lib/common/plugin-api-rpc.js.map +1 -1
  12. package/lib/common/plugin-protocol.d.ts +2 -2
  13. package/lib/common/plugin-protocol.d.ts.map +1 -1
  14. package/lib/common/plugin-protocol.js +2 -1
  15. package/lib/common/plugin-protocol.js.map +1 -1
  16. package/lib/hosted/browser/hosted-plugin.d.ts +2 -0
  17. package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
  18. package/lib/hosted/browser/hosted-plugin.js +9 -3
  19. package/lib/hosted/browser/hosted-plugin.js.map +1 -1
  20. package/lib/hosted/browser/worker/worker-main.js +22 -12
  21. package/lib/hosted/browser/worker/worker-main.js.map +1 -1
  22. package/lib/hosted/browser/worker/worker-plugin-module.d.ts.map +1 -1
  23. package/lib/hosted/browser/worker/worker-plugin-module.js +2 -0
  24. package/lib/hosted/browser/worker/worker-plugin-module.js.map +1 -1
  25. package/lib/hosted/browser-only/browser-only-hosted-plugin-server.d.ts +37 -0
  26. package/lib/hosted/browser-only/browser-only-hosted-plugin-server.d.ts.map +1 -0
  27. package/lib/hosted/browser-only/browser-only-hosted-plugin-server.js +107 -0
  28. package/lib/hosted/browser-only/browser-only-hosted-plugin-server.js.map +1 -0
  29. package/lib/hosted/browser-only/browser-only-hosted-plugin-server.spec.d.ts +2 -0
  30. package/lib/hosted/browser-only/browser-only-hosted-plugin-server.spec.d.ts.map +1 -0
  31. package/lib/hosted/browser-only/browser-only-hosted-plugin-server.spec.js +106 -0
  32. package/lib/hosted/browser-only/browser-only-hosted-plugin-server.spec.js.map +1 -0
  33. package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.d.ts +38 -0
  34. package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.d.ts.map +1 -0
  35. package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.js +124 -0
  36. package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.js.map +1 -0
  37. package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.spec.d.ts +2 -0
  38. package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.spec.d.ts.map +1 -0
  39. package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.spec.js +137 -0
  40. package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.spec.js.map +1 -0
  41. package/lib/hosted/browser-only/browser-only-plugin-paths-service.d.ts +43 -0
  42. package/lib/hosted/browser-only/browser-only-plugin-paths-service.d.ts.map +1 -0
  43. package/lib/hosted/browser-only/browser-only-plugin-paths-service.js +145 -0
  44. package/lib/hosted/browser-only/browser-only-plugin-paths-service.js.map +1 -0
  45. package/lib/hosted/browser-only/browser-only-plugin-paths-service.spec.d.ts +2 -0
  46. package/lib/hosted/browser-only/browser-only-plugin-paths-service.spec.d.ts.map +1 -0
  47. package/lib/hosted/browser-only/browser-only-plugin-paths-service.spec.js +165 -0
  48. package/lib/hosted/browser-only/browser-only-plugin-paths-service.spec.js.map +1 -0
  49. package/lib/hosted/browser-only/browser-only-plugin-server.d.ts +54 -0
  50. package/lib/hosted/browser-only/browser-only-plugin-server.d.ts.map +1 -0
  51. package/lib/hosted/browser-only/browser-only-plugin-server.js +155 -0
  52. package/lib/hosted/browser-only/browser-only-plugin-server.js.map +1 -0
  53. package/lib/hosted/browser-only/browser-only-plugin-server.spec.d.ts +2 -0
  54. package/lib/hosted/browser-only/browser-only-plugin-server.spec.d.ts.map +1 -0
  55. package/lib/hosted/browser-only/browser-only-plugin-server.spec.js +190 -0
  56. package/lib/hosted/browser-only/browser-only-plugin-server.spec.js.map +1 -0
  57. package/lib/hosted/browser-only/test/navigator-locks-test-util.d.ts +24 -0
  58. package/lib/hosted/browser-only/test/navigator-locks-test-util.d.ts.map +1 -0
  59. package/lib/hosted/browser-only/test/navigator-locks-test-util.js +46 -0
  60. package/lib/hosted/browser-only/test/navigator-locks-test-util.js.map +1 -0
  61. package/lib/hosted/common/hosted-plugin.d.ts +2 -2
  62. package/lib/hosted/common/hosted-plugin.d.ts.map +1 -1
  63. package/lib/hosted/common/hosted-plugin.js +2 -2
  64. package/lib/hosted/common/hosted-plugin.js.map +1 -1
  65. package/lib/hosted/node/plugin-host-module.d.ts.map +1 -1
  66. package/lib/hosted/node/plugin-host-module.js +2 -0
  67. package/lib/hosted/node/plugin-host-module.js.map +1 -1
  68. package/lib/hosted/node/plugin-host-rpc.d.ts +3 -0
  69. package/lib/hosted/node/plugin-host-rpc.d.ts.map +1 -1
  70. package/lib/hosted/node/plugin-host-rpc.js +9 -3
  71. package/lib/hosted/node/plugin-host-rpc.js.map +1 -1
  72. package/lib/main/browser/editors-and-documents-main.d.ts +7 -0
  73. package/lib/main/browser/editors-and-documents-main.d.ts.map +1 -1
  74. package/lib/main/browser/editors-and-documents-main.js +19 -9
  75. package/lib/main/browser/editors-and-documents-main.js.map +1 -1
  76. package/lib/main/browser/editors-and-documents-main.spec.d.ts +2 -0
  77. package/lib/main/browser/editors-and-documents-main.spec.d.ts.map +1 -0
  78. package/lib/main/browser/editors-and-documents-main.spec.js +117 -0
  79. package/lib/main/browser/editors-and-documents-main.spec.js.map +1 -0
  80. package/lib/main/browser/languages-main.d.ts +7 -0
  81. package/lib/main/browser/languages-main.d.ts.map +1 -1
  82. package/lib/main/browser/languages-main.js +31 -9
  83. package/lib/main/browser/languages-main.js.map +1 -1
  84. package/lib/main/browser/languages-main.spec.d.ts +2 -0
  85. package/lib/main/browser/languages-main.spec.d.ts.map +1 -0
  86. package/lib/main/browser/languages-main.spec.js +82 -0
  87. package/lib/main/browser/languages-main.spec.js.map +1 -0
  88. package/lib/main/browser/main-context.d.ts.map +1 -1
  89. package/lib/main/browser/main-context.js +3 -0
  90. package/lib/main/browser/main-context.js.map +1 -1
  91. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +5 -0
  92. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
  93. package/lib/main/browser/menus/vscode-theia-menu-mappings.js +5 -0
  94. package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
  95. package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
  96. package/lib/main/browser/plugin-ext-frontend-module.js +1 -0
  97. package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
  98. package/lib/main/browser/plugin-icon-service.d.ts +6 -0
  99. package/lib/main/browser/plugin-icon-service.d.ts.map +1 -1
  100. package/lib/main/browser/plugin-icon-service.js +14 -2
  101. package/lib/main/browser/plugin-icon-service.js.map +1 -1
  102. package/lib/main/browser/plugin-icon-service.spec.d.ts +2 -0
  103. package/lib/main/browser/plugin-icon-service.spec.d.ts.map +1 -0
  104. package/lib/main/browser/plugin-icon-service.spec.js +83 -0
  105. package/lib/main/browser/plugin-icon-service.spec.js.map +1 -0
  106. package/lib/main/browser/plugin-icon-theme-service.d.ts.map +1 -1
  107. package/lib/main/browser/plugin-icon-theme-service.js +2 -1
  108. package/lib/main/browser/plugin-icon-theme-service.js.map +1 -1
  109. package/lib/main/browser/plugin-icon-theme-service.spec.d.ts +2 -0
  110. package/lib/main/browser/plugin-icon-theme-service.spec.d.ts.map +1 -0
  111. package/lib/main/browser/plugin-icon-theme-service.spec.js +91 -0
  112. package/lib/main/browser/plugin-icon-theme-service.spec.js.map +1 -0
  113. package/lib/main/browser/telemetry-main.d.ts +20 -0
  114. package/lib/main/browser/telemetry-main.d.ts.map +1 -0
  115. package/lib/main/browser/telemetry-main.js +43 -0
  116. package/lib/main/browser/telemetry-main.js.map +1 -0
  117. package/lib/main/browser/telemetry-main.spec.d.ts +2 -0
  118. package/lib/main/browser/telemetry-main.spec.d.ts.map +1 -0
  119. package/lib/main/browser/telemetry-main.spec.js +66 -0
  120. package/lib/main/browser/telemetry-main.spec.js.map +1 -0
  121. package/lib/main/browser/webview/webview-context-keys.d.ts +3 -2
  122. package/lib/main/browser/webview/webview-context-keys.d.ts.map +1 -1
  123. package/lib/main/browser/webview/webview-context-keys.js +16 -2
  124. package/lib/main/browser/webview/webview-context-keys.js.map +1 -1
  125. package/lib/main/browser/webview/webview-environment.d.ts.map +1 -1
  126. package/lib/main/browser/webview/webview-environment.js +2 -1
  127. package/lib/main/browser/webview/webview-environment.js.map +1 -1
  128. package/lib/main/common/paths/const.d.ts +13 -0
  129. package/lib/main/common/paths/const.d.ts.map +1 -0
  130. package/lib/main/common/paths/const.js +32 -0
  131. package/lib/main/common/paths/const.js.map +1 -0
  132. package/lib/main/node/paths/const.d.ts +5 -5
  133. package/lib/main/node/paths/const.d.ts.map +1 -1
  134. package/lib/main/node/paths/const.js +6 -6
  135. package/lib/main/node/paths/const.js.map +1 -1
  136. package/lib/main/node/paths/plugin-paths-service.js +1 -1
  137. package/lib/main/node/paths/plugin-paths-service.js.map +1 -1
  138. package/lib/main/node/plugin-cli-contribution.d.ts +2 -2
  139. package/lib/main/node/plugin-cli-contribution.d.ts.map +1 -1
  140. package/lib/main/node/plugin-cli-contribution.js +4 -3
  141. package/lib/main/node/plugin-cli-contribution.js.map +1 -1
  142. package/lib/main/node/plugins-key-value-storage.js +1 -1
  143. package/lib/main/node/plugins-key-value-storage.js.map +1 -1
  144. package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.d.ts +5 -0
  145. package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.d.ts.map +1 -1
  146. package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.js +12 -1
  147. package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.js.map +1 -1
  148. package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.d.ts +2 -0
  149. package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.d.ts.map +1 -0
  150. package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.js +54 -0
  151. package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.js.map +1 -0
  152. package/lib/plugin/plugin-context.d.ts +2 -1
  153. package/lib/plugin/plugin-context.d.ts.map +1 -1
  154. package/lib/plugin/plugin-context.js +2 -3
  155. package/lib/plugin/plugin-context.js.map +1 -1
  156. package/lib/plugin/plugin-manager.d.ts +2 -0
  157. package/lib/plugin/plugin-manager.d.ts.map +1 -1
  158. package/lib/plugin/plugin-manager.js +12 -1
  159. package/lib/plugin/plugin-manager.js.map +1 -1
  160. package/lib/plugin/relative-pattern.spec.d.ts +2 -0
  161. package/lib/plugin/relative-pattern.spec.d.ts.map +1 -0
  162. package/lib/plugin/relative-pattern.spec.js +51 -0
  163. package/lib/plugin/relative-pattern.spec.js.map +1 -0
  164. package/lib/plugin/telemetry-ext.d.ts +23 -7
  165. package/lib/plugin/telemetry-ext.d.ts.map +1 -1
  166. package/lib/plugin/telemetry-ext.js +60 -33
  167. package/lib/plugin/telemetry-ext.js.map +1 -1
  168. package/lib/plugin/telemetry-ext.spec.js +121 -16
  169. package/lib/plugin/telemetry-ext.spec.js.map +1 -1
  170. package/lib/plugin-ext-frontend-only-module.d.ts +4 -0
  171. package/lib/plugin-ext-frontend-only-module.d.ts.map +1 -0
  172. package/lib/plugin-ext-frontend-only-module.js +34 -0
  173. package/lib/plugin-ext-frontend-only-module.js.map +1 -0
  174. package/package.json +36 -32
  175. package/src/common/paths-util.spec.ts +47 -0
  176. package/src/common/paths-util.ts +11 -2
  177. package/src/common/plugin-api-rpc.ts +15 -1
  178. package/src/common/plugin-protocol.ts +2 -0
  179. package/src/hosted/browser/hosted-plugin.ts +9 -4
  180. package/src/hosted/browser/worker/worker-main.ts +23 -12
  181. package/src/hosted/browser/worker/worker-plugin-module.ts +2 -0
  182. package/src/hosted/browser-only/browser-only-hosted-plugin-server.spec.ts +133 -0
  183. package/src/hosted/browser-only/browser-only-hosted-plugin-server.ts +122 -0
  184. package/src/hosted/browser-only/browser-only-plugin-file-system-provider.spec.ts +178 -0
  185. package/src/hosted/browser-only/browser-only-plugin-file-system-provider.ts +129 -0
  186. package/src/hosted/browser-only/browser-only-plugin-paths-service.spec.ts +199 -0
  187. package/src/hosted/browser-only/browser-only-plugin-paths-service.ts +145 -0
  188. package/src/hosted/browser-only/browser-only-plugin-server.spec.ts +228 -0
  189. package/src/hosted/browser-only/browser-only-plugin-server.ts +165 -0
  190. package/src/hosted/browser-only/test/navigator-locks-test-util.ts +54 -0
  191. package/src/hosted/common/hosted-plugin.ts +4 -4
  192. package/src/hosted/node/plugin-host-module.ts +2 -0
  193. package/src/hosted/node/plugin-host-rpc.ts +10 -4
  194. package/src/main/browser/editors-and-documents-main.spec.ts +140 -0
  195. package/src/main/browser/editors-and-documents-main.ts +19 -7
  196. package/src/main/browser/languages-main.spec.ts +101 -0
  197. package/src/main/browser/languages-main.ts +31 -9
  198. package/src/main/browser/main-context.ts +4 -0
  199. package/src/main/browser/menus/vscode-theia-menu-mappings.ts +5 -0
  200. package/src/main/browser/plugin-ext-frontend-module.ts +2 -1
  201. package/src/main/browser/plugin-icon-service.spec.ts +101 -0
  202. package/src/main/browser/plugin-icon-service.ts +16 -3
  203. package/src/main/browser/plugin-icon-theme-service.spec.ts +103 -0
  204. package/src/main/browser/plugin-icon-theme-service.ts +2 -1
  205. package/src/main/browser/telemetry-main.spec.ts +77 -0
  206. package/src/main/browser/telemetry-main.ts +46 -0
  207. package/src/main/browser/webview/webview-context-keys.ts +20 -4
  208. package/src/main/browser/webview/webview-environment.ts +2 -1
  209. package/src/main/common/paths/const.ts +28 -0
  210. package/src/main/node/paths/const.ts +5 -5
  211. package/src/main/node/paths/plugin-paths-service.ts +1 -1
  212. package/src/main/node/plugin-cli-contribution.ts +4 -3
  213. package/src/main/node/plugins-key-value-storage.ts +1 -1
  214. package/src/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.ts +61 -0
  215. package/src/main/node/resolvers/local-directory-plugin-deployer-resolver.ts +14 -1
  216. package/src/plugin/plugin-context.ts +3 -2
  217. package/src/plugin/plugin-manager.ts +11 -1
  218. package/src/plugin/relative-pattern.spec.ts +55 -0
  219. package/src/plugin/telemetry-ext.spec.ts +158 -17
  220. package/src/plugin/telemetry-ext.ts +63 -33
  221. package/src/plugin-ext-frontend-only-module.ts +34 -0
@@ -26,6 +26,7 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'
26
26
  import { IconThemeService, IconTheme, IconThemeDefinition } from '@theia/core/lib/browser/icon-theme-service';
27
27
  import { IconThemeContribution, DeployedPlugin, UiTheme, getPluginId } from '../../common/plugin-protocol';
28
28
  import { PLUGINS_BASE_PATH } from '@theia/plugin-utils/lib/common/constants';
29
+ import { encodePluginAssetPath } from '@theia/plugin-utils/lib/common/plugin-model';
29
30
  import URI from '@theia/core/lib/common/uri';
30
31
  import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
31
32
  import { Emitter } from '@theia/core/lib/common/event';
@@ -364,7 +365,7 @@ export class PluginIconTheme extends PluginIconThemeDefinition implements IconTh
364
365
  const iconUri = this.locationUri.resolve(iconPath);
365
366
  const relativePath = this.packageRootUri.path.relative(iconUri.path.normalize());
366
367
  return relativePath && `url('${new Endpoint({
367
- path: `${PLUGINS_BASE_PATH}/${this.pluginId}/${encodeURIComponent(relativePath.normalize().toString())}`
368
+ path: `${PLUGINS_BASE_PATH}/${this.pluginId}/${encodePluginAssetPath(relativePath.normalize().toString())}`
368
369
  }).getRestUrl().toString()}')`;
369
370
  }
370
371
 
@@ -0,0 +1,77 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 JuliaHub, Inc. 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 { Emitter } from '@theia/core/lib/common/event';
19
+ import { Container } from '@theia/core/shared/inversify';
20
+ import { TelemetryConsentProvider } from '@theia/telemetry/lib/common/telemetry-consent-provider';
21
+ import { TelemetryLevel } from '@theia/telemetry/lib/common/telemetry-types';
22
+ import { RPCProtocol } from '../../common/rpc-protocol';
23
+ import { TelemetryMainImpl } from './telemetry-main';
24
+
25
+ describe('TelemetryMainImpl', () => {
26
+
27
+ let level: TelemetryLevel;
28
+ let pushed: TelemetryLevel[];
29
+ let levelChanged: Emitter<TelemetryLevel>;
30
+ let container: Container;
31
+ let rpc: RPCProtocol;
32
+
33
+ beforeEach(() => {
34
+ level = 'off';
35
+ pushed = [];
36
+ levelChanged = new Emitter<TelemetryLevel>();
37
+ container = new Container();
38
+ container.bind(TelemetryConsentProvider).toConstantValue(<TelemetryConsentProvider>{
39
+ get level(): TelemetryLevel { return level; },
40
+ onDidChangeTelemetryLevel: levelChanged.event,
41
+ ready: Promise.resolve()
42
+ });
43
+ rpc = {
44
+ getProxy<T>(_proxyId: unknown): T {
45
+ return {
46
+ $onDidChangeTelemetryLevel: (next: TelemetryLevel) => { pushed.push(next); }
47
+ } as unknown as T;
48
+ },
49
+ set: <T, R extends T>(_identifier: unknown, instance: R) => instance,
50
+ dispose: () => { }
51
+ } as RPCProtocol;
52
+ });
53
+
54
+ it('does not push a level on construction, the initial level is seeded through $init', () => {
55
+ new TelemetryMainImpl(rpc, container);
56
+
57
+ expect(pushed).to.be.empty;
58
+ });
59
+
60
+ it('pushes every level change to the plugin host', () => {
61
+ new TelemetryMainImpl(rpc, container);
62
+
63
+ levelChanged.fire('all');
64
+ levelChanged.fire('error');
65
+
66
+ expect(pushed).to.deep.equal(['all', 'error']);
67
+ });
68
+
69
+ it('stops pushing once disposed', () => {
70
+ const telemetryMain = new TelemetryMainImpl(rpc, container);
71
+
72
+ telemetryMain.dispose();
73
+ levelChanged.fire('all');
74
+
75
+ expect(pushed).to.be.empty;
76
+ });
77
+ });
@@ -0,0 +1,46 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 JuliaHub, Inc. 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 { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
18
+ import { interfaces } from '@theia/core/shared/inversify';
19
+ import { TelemetryConsentProvider } from '@theia/telemetry/lib/common/telemetry-consent-provider';
20
+ import { MAIN_RPC_CONTEXT, TelemetryExt, TelemetryMain } from '../../common/plugin-api-rpc';
21
+ import { RPCProtocol } from '../../common/rpc-protocol';
22
+
23
+ /**
24
+ * Forwards the user's telemetry consent to the plugin host. The initial level is seeded through
25
+ * `PluginManagerInitializeParams` so that it is in place before any plugin activates.
26
+ *
27
+ * `TelemetryMain` declares no `$` methods; the instance is registered with the RPC protocol so that the consent
28
+ * subscription is disposed along with the channel.
29
+ *
30
+ * @experimental
31
+ */
32
+ export class TelemetryMainImpl implements TelemetryMain, Disposable {
33
+
34
+ protected readonly proxy: TelemetryExt;
35
+ protected readonly toDispose = new DisposableCollection();
36
+
37
+ constructor(rpc: RPCProtocol, container: interfaces.Container) {
38
+ this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TELEMETRY_EXT);
39
+ const consentProvider = container.get<TelemetryConsentProvider>(TelemetryConsentProvider);
40
+ this.toDispose.push(consentProvider.onDidChangeTelemetryLevel(level => this.proxy.$onDidChangeTelemetryLevel(level)));
41
+ }
42
+
43
+ dispose(): void {
44
+ this.toDispose.dispose();
45
+ }
46
+ }
@@ -14,14 +14,18 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { ApplicationShell, FocusTracker, Widget } from '@theia/core/lib/browser';
17
+ import { ApplicationShell, FocusTracker, Widget, WidgetContextKeyContribution } from '@theia/core/lib/browser';
18
18
  import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service';
19
19
  import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
20
20
  import { CustomEditorWidget } from '../custom-editors/custom-editor-widget';
21
+ import { CodeEditorWidgetUtil } from '../menus/vscode-theia-menu-mappings';
21
22
  import { WebviewWidget } from './webview';
22
23
 
24
+ const ACTIVE_WEBVIEW_PANEL_ID = 'activeWebviewPanelId';
25
+ const ACTIVE_CUSTOM_EDITOR_ID = 'activeCustomEditorId';
26
+
23
27
  @injectable()
24
- export class WebviewContextKeys {
28
+ export class WebviewContextKeys implements WidgetContextKeyContribution {
25
29
 
26
30
  /**
27
31
  * Context key representing the `viewType` of the active `WebviewWidget`, if any.
@@ -41,11 +45,23 @@ export class WebviewContextKeys {
41
45
 
42
46
  @postConstruct()
43
47
  protected init(): void {
44
- this.activeWebviewPanelId = this.contextKeyService.createKey('activeWebviewPanelId', '');
45
- this.activeCustomEditorId = this.contextKeyService.createKey('activeCustomEditorId', '');
48
+ this.activeWebviewPanelId = this.contextKeyService.createKey(ACTIVE_WEBVIEW_PANEL_ID, '');
49
+ this.activeCustomEditorId = this.contextKeyService.createKey(ACTIVE_CUSTOM_EDITOR_ID, '');
46
50
  this.applicationShell.onDidChangeCurrentWidget(this.handleDidChangeCurrentWidget, this);
47
51
  }
48
52
 
53
+ getContextKeyValues(widget: Widget): Iterable<[string, unknown]> | undefined {
54
+ // A view toolbar item may key on the *active* editor, so only the editor-like widgets these keys can
55
+ // describe get an answer; everything else keeps the ambient value.
56
+ if (!CodeEditorWidgetUtil.is(widget)) {
57
+ return undefined;
58
+ }
59
+ return [
60
+ [ACTIVE_CUSTOM_EDITOR_ID, widget instanceof CustomEditorWidget ? widget.viewType : ''],
61
+ [ACTIVE_WEBVIEW_PANEL_ID, widget instanceof WebviewWidget ? widget.viewType : '']
62
+ ];
63
+ }
64
+
49
65
  protected handleDidChangeCurrentWidget(change: FocusTracker.IChangedArgs<Widget>): void {
50
66
  const { newValue } = change;
51
67
  if (newValue instanceof CustomEditorWidget) {
@@ -20,6 +20,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
20
20
  import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
21
21
  import URI from '@theia/core/lib/common/uri';
22
22
  import { WebviewExternalEndpoint } from '../../common/webview-protocol';
23
+ import { PLUGIN_HOST_FRONTEND } from '../../../common/plugin-protocol';
23
24
  import { environment } from '@theia/core/shared/@theia/application-package/lib/environment';
24
25
 
25
26
  @injectable()
@@ -65,7 +66,7 @@ export class WebviewEnvironment {
65
66
  }
66
67
 
67
68
  async resourceRoot(host: string): Promise<string> {
68
- if (host === 'frontend') {
69
+ if (host === PLUGIN_HOST_FRONTEND) {
69
70
  return (await this.externalEndpointUrl()).withPath('{{path}}').toString(true);
70
71
  }
71
72
 
@@ -0,0 +1,28 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2018 Red Hat, Inc. 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
+ export namespace PluginPaths {
18
+ export const PLUGINS_LOGS_DIR = 'logs';
19
+ export const PLUGINS_GLOBAL_STORAGE_DIR = 'plugin-storage';
20
+ export const PLUGINS_WORKSPACE_STORAGE_DIR = 'workspace-storage';
21
+ /**
22
+ * Default number of per-session plugin log folders to retain. Shared between the backend's
23
+ * `--plugin-max-session-logs-folders` CLI option and the browser-only frontend, which has no
24
+ * CLI to read it from. This is the default value used in VSCode, see:
25
+ * https://github.com/Microsoft/vscode/blob/613447d6b3f458ef7fee227e3876303bf5184580/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts#L32
26
+ */
27
+ export const DEFAULT_PLUGIN_MAX_SESSION_LOGS_FOLDERS = 10;
28
+ }
@@ -14,8 +14,8 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- export namespace PluginPaths {
18
- export const PLUGINS_LOGS_DIR = 'logs';
19
- export const PLUGINS_GLOBAL_STORAGE_DIR = 'plugin-storage';
20
- export const PLUGINS_WORKSPACE_STORAGE_DIR = 'workspace-storage';
21
- }
17
+ /**
18
+ * @deprecated since 1.76.0 - `PluginPaths` moved to `@theia/plugin-ext/lib/main/common/paths/const`
19
+ * so the browser-only frontend can share it. Import it from there.
20
+ */
21
+ export { PluginPaths } from '../../common/paths/const';
@@ -23,7 +23,7 @@ import { remove } from '@theia/core/shared/fs-extra';
23
23
  import * as crypto from 'crypto';
24
24
  import { ILogger } from '@theia/core';
25
25
  import { FileUri } from '@theia/core/lib/node';
26
- import { PluginPaths } from './const';
26
+ import { PluginPaths } from '../../common/paths/const';
27
27
  import { PluginPathsService } from '../../common/plugin-paths-protocol';
28
28
  import { UntitledWorkspaceService } from '@theia/workspace/lib/common';
29
29
  import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
@@ -18,6 +18,7 @@ import { injectable } from '@theia/core/shared/inversify';
18
18
  import { Argv, Arguments } from '@theia/core/shared/yargs';
19
19
  import { CliContribution } from '@theia/core/lib/node/cli';
20
20
  import { LocalDirectoryPluginDeployerResolver } from './resolvers/local-directory-plugin-deployer-resolver';
21
+ import { PluginPaths } from '../common/paths/const';
21
22
 
22
23
  @injectable()
23
24
  export class PluginCliContribution implements CliContribution {
@@ -26,10 +27,10 @@ export class PluginCliContribution implements CliContribution {
26
27
  static PLUGIN_MAX_SESSION_LOGS_FOLDERS = 'plugin-max-session-logs-folders';
27
28
  static UNCOMPRESSED_PLUGINS_IN_PLACE = 'uncompressed-plugins-in-place';
28
29
  /**
29
- * This is the default value used in VSCode, see:
30
- * - https://github.com/Microsoft/vscode/blob/613447d6b3f458ef7fee227e3876303bf5184580/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts#L32
30
+ * Shared with the browser-only frontend (which has no CLI to read this option from) via
31
+ * {@link PluginPaths.DEFAULT_PLUGIN_MAX_SESSION_LOGS_FOLDERS}, so the two defaults cannot drift apart.
31
32
  */
32
- static DEFAULT_PLUGIN_MAX_SESSION_LOGS_FOLDERS = 10;
33
+ static DEFAULT_PLUGIN_MAX_SESSION_LOGS_FOLDERS = PluginPaths.DEFAULT_PLUGIN_MAX_SESSION_LOGS_FOLDERS;
33
34
 
34
35
  protected _localDir: string | undefined;
35
36
  protected _maxSessionLogsFolders: number;
@@ -21,7 +21,7 @@ import * as path from 'path';
21
21
  import { FileUri } from '@theia/core/lib/common/file-uri';
22
22
  import { Deferred } from '@theia/core/lib/common/promise-util';
23
23
  import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
24
- import { PluginPaths } from './paths/const';
24
+ import { PluginPaths } from '../common/paths/const';
25
25
  import { PluginPathsService } from '../common/plugin-paths-protocol';
26
26
  import { KeysToAnyValues, KeysToKeysToAnyValue } from '../../common/types';
27
27
  import { PluginStorageKind } from '../../common';
@@ -0,0 +1,61 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 Matthew Farrow 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 * as fs from 'fs';
19
+ import * as os from 'os';
20
+ import * as path from 'path';
21
+ import { LocalDirectoryPluginDeployerResolver } from './local-directory-plugin-deployer-resolver';
22
+ import { PluginDeployerResolverContext } from '../../../common/plugin-protocol';
23
+
24
+ describe('LocalDirectoryPluginDeployerResolver', () => {
25
+
26
+ let localPath: string;
27
+ let deployed: Map<string, string>;
28
+
29
+ beforeEach(() => {
30
+ localPath = fs.mkdtempSync(path.join(os.tmpdir(), 'theia-local-dir-'));
31
+ deployed = new Map();
32
+ });
33
+
34
+ afterEach(() => {
35
+ fs.rmSync(localPath, { recursive: true, force: true });
36
+ });
37
+
38
+ function createContext(): PluginDeployerResolverContext {
39
+ return {
40
+ getOriginId: () => `${LocalDirectoryPluginDeployerResolver.LOCAL_DIR}:${localPath}`,
41
+ addPlugin: (pluginId: string, pluginPath: string) => {
42
+ deployed.set(pluginId, pluginPath);
43
+ }
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ } as any as PluginDeployerResolverContext;
46
+ }
47
+
48
+ it('should skip files that are not plugins', async () => {
49
+ fs.mkdirSync(path.join(localPath, 'my.plugin'));
50
+ fs.writeFileSync(path.join(localPath, 'my-other.plugin.vsix'), '');
51
+ fs.writeFileSync(path.join(localPath, '.DS_Store'), '');
52
+ fs.writeFileSync(path.join(localPath, '._my.plugin'), '');
53
+ fs.writeFileSync(path.join(localPath, 'Thumbs.db'), '');
54
+ fs.writeFileSync(path.join(localPath, 'desktop.ini'), '');
55
+
56
+ await new LocalDirectoryPluginDeployerResolver().resolve(createContext());
57
+
58
+ expect([...deployed.keys()].sort()).to.deep.equal(['my-other.plugin.vsix', 'my.plugin']);
59
+ expect(deployed.get('my.plugin')).to.equal(path.resolve(localPath, 'my.plugin'));
60
+ });
61
+ });
@@ -20,6 +20,11 @@ import * as fs from '@theia/core/shared/fs-extra';
20
20
  import * as path from 'path';
21
21
  import { LocalPluginDeployerResolver } from './local-plugin-deployer-resolver';
22
22
 
23
+ /**
24
+ * Lower-case names of files a file manager leaves behind, which are never plugins.
25
+ */
26
+ const IGNORED_FILE_NAMES = new Set(['thumbs.db', 'desktop.ini']);
27
+
23
28
  @injectable()
24
29
  export class LocalDirectoryPluginDeployerResolver extends LocalPluginDeployerResolver {
25
30
  static LOCAL_DIR = 'local-dir';
@@ -30,8 +35,16 @@ export class LocalDirectoryPluginDeployerResolver extends LocalPluginDeployerRes
30
35
 
31
36
  protected async resolveFromLocalPath(pluginResolverContext: PluginDeployerResolverContext, localPath: string): Promise<void> {
32
37
  const files = await fs.readdir(localPath);
33
- files.forEach(file =>
38
+ files.filter(file => !this.isIgnored(file)).forEach(file =>
34
39
  pluginResolverContext.addPlugin(file, path.resolve(localPath, file))
35
40
  );
36
41
  }
42
+
43
+ /**
44
+ * Whether a directory entry should be skipped instead of being deployed as a plugin.
45
+ * Covers dotfiles such as the macOS `.DS_Store` and the Windows `Thumbs.db` and `desktop.ini`.
46
+ */
47
+ protected isIgnored(file: string): boolean {
48
+ return file.startsWith('.') || IGNORED_FILE_NAMES.has(file.toLowerCase());
49
+ }
37
50
  }
@@ -325,7 +325,8 @@ export function createAPIFactory(
325
325
  messageRegistryExt: MessageRegistryExt,
326
326
  clipboard: ClipboardExt,
327
327
  webviewExt: WebviewsExtImpl,
328
- localizationExt: LocalizationExtImpl
328
+ localizationExt: LocalizationExtImpl,
329
+ telemetryExt: TelemetryExtImpl
329
330
  ): PluginAPIFactory {
330
331
 
331
332
  const authenticationExt = rpc.set(MAIN_RPC_CONTEXT.AUTHENTICATION_EXT, new AuthenticationExtImpl(rpc));
@@ -359,12 +360,12 @@ export function createAPIFactory(
359
360
  const tabsExt = rpc.set(MAIN_RPC_CONTEXT.TABS_EXT, new TabsExtImpl(rpc));
360
361
  const customEditorExt = rpc.set(MAIN_RPC_CONTEXT.CUSTOM_EDITORS_EXT, new CustomEditorsExtImpl(rpc, documents, webviewExt));
361
362
  const webviewViewsExt = rpc.set(MAIN_RPC_CONTEXT.WEBVIEW_VIEWS_EXT, new WebviewViewsExtImpl(rpc, webviewExt));
362
- const telemetryExt = rpc.set(MAIN_RPC_CONTEXT.TELEMETRY_EXT, new TelemetryExtImpl());
363
363
  const testingExt = rpc.set(MAIN_RPC_CONTEXT.TESTING_EXT, new TestingExtImpl(rpc, commandRegistry));
364
364
  const uriExt = rpc.set(MAIN_RPC_CONTEXT.URI_EXT, new UriExtImpl(rpc));
365
365
  const lmExt = rpc.set(MAIN_RPC_CONTEXT.MCP_SERVER_DEFINITION_REGISTRY_EXT, new LmExtImpl(rpc));
366
366
  const lmToolExt = rpc.set(MAIN_RPC_CONTEXT.LM_TOOLS_EXT, new LanguageModelToolsExtImpl(rpc));
367
367
  rpc.set(MAIN_RPC_CONTEXT.DEBUG_EXT, debugExt);
368
+ rpc.set(MAIN_RPC_CONTEXT.TELEMETRY_EXT, telemetryExt);
368
369
 
369
370
  const commandLogger = new PluginLogger(rpc, 'commands-plugin');
370
371
 
@@ -37,6 +37,7 @@ import * as theia from '@theia/plugin';
37
37
  import * as types from './types-impl';
38
38
  import { join } from './path';
39
39
  import { EnvExtImpl } from './env';
40
+ import { TelemetryExtImpl } from './telemetry-ext';
40
41
  import { PreferenceRegistryExtImpl } from './preference-registry';
41
42
  import { InternalStorageExt, Memento, GlobalState } from './plugin-storage';
42
43
  import { ExtPluginApi } from '../common/plugin-ext-api-contribution';
@@ -244,8 +245,12 @@ export abstract class AbstractPluginManagerExtImpl<P extends Record<string, any>
244
245
  contributes.jsonValidation = (contributes.jsonValidation || []).concat(this.jsonValidation);
245
246
  }
246
247
  this.registry.set(plugin.model.id, plugin);
248
+ if (!plugin.pluginPath) {
249
+ // No entry point at all, so nothing to activate here.
250
+ return;
251
+ }
247
252
  const activationEvents = this.getActivationEvents(plugin);
248
- if (plugin.pluginPath && activationEvents) {
253
+ if (activationEvents) {
249
254
  const activation = () => this.$activatePlugin(plugin.model.id);
250
255
  // an internal activation event is a subject to change
251
256
  this.setActivation(`onPlugin:${plugin.model.id}`, activation);
@@ -474,6 +479,9 @@ export class PluginManagerExtImpl extends AbstractPluginManagerExtImpl<PluginMan
474
479
  @inject(WebviewsExtImpl)
475
480
  protected readonly webview: WebviewsExtImpl;
476
481
 
482
+ @inject(TelemetryExtImpl)
483
+ protected readonly telemetryExt: TelemetryExtImpl;
484
+
477
485
  private supportedActivationEvents: Set<string>;
478
486
 
479
487
  async $init(params: PluginManagerInitializeParams): Promise<void> {
@@ -489,6 +497,8 @@ export class PluginManagerExtImpl extends AbstractPluginManagerExtImpl<PluginMan
489
497
  this.envExt.setAppUriScheme(params.env.appUriScheme);
490
498
 
491
499
  this.preferencesManager.init(params.preferences);
500
+ // must be in place before activation: plugins create their telemetry loggers during `activate()`
501
+ this.telemetryExt.setLevel(params.telemetryLevel ?? 'off');
492
502
 
493
503
  if (params.extApi) {
494
504
  this.host.initExtApi(params.extApi);
@@ -0,0 +1,55 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 JuliaHub, Inc. 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 * as os from 'os';
19
+ import * as path from 'path';
20
+ import { parse } from '@theia/core/lib/common/glob';
21
+ import { isWindows } from '@theia/core/lib/common/os';
22
+ import { URI } from '@theia/core/shared/vscode-uri';
23
+ import { RelativePattern } from './types-impl';
24
+
25
+ // This is the matching a `FileSystemWatcher` performs on every incoming event:
26
+ // `ExtHostFileSystemEventService` builds `parse(globPattern)` once and applies it
27
+ // to each event's `fsPath`. `wrapRelativePattern` rejects a base that is not a
28
+ // parent of the path under `nativeSep`, so a backslash-separated base is only
29
+ // reachable on Windows — elsewhere the pattern is rejected before the glob runs.
30
+ (isWindows ? describe : describe.skip)('RelativePattern matching against native Windows paths', () => {
31
+
32
+ // Derive the child paths from the URI's own `fsPath`: `Uri.file` lower-cases the
33
+ // drive letter, and the parent check is case-sensitive.
34
+ const base = URI.file(path.join(os.tmpdir(), 'theia-relative-pattern'));
35
+
36
+ it('should match a file named by a bare-filename pattern', () => {
37
+ const matches = parse(new RelativePattern(base, 'Project.toml'));
38
+ expect(matches(path.join(base.fsPath, 'Project.toml'))).to.be.ok;
39
+ });
40
+
41
+ it('should not match a different file in the same folder', () => {
42
+ const matches = parse(new RelativePattern(base, 'Project.toml'));
43
+ expect(matches(path.join(base.fsPath, 'Manifest.toml'))).to.not.be.ok;
44
+ });
45
+
46
+ it('should match a nested file named by a subpath pattern', () => {
47
+ const matches = parse(new RelativePattern(base, 'generated/*.jl'));
48
+ expect(matches(path.join(base.fsPath, 'generated', 'definitions.jl'))).to.be.ok;
49
+ });
50
+
51
+ it('should not match a file outside the subpath of the pattern', () => {
52
+ const matches = parse(new RelativePattern(base, 'generated/*.jl'));
53
+ expect(matches(path.join(base.fsPath, 'definitions.jl'))).to.not.be.ok;
54
+ });
55
+ });