@theia/plugin-ext 1.76.0-next.7 → 1.77.0-next.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
@@ -15,47 +15,188 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import * as chai from 'chai';
18
+ import { TelemetryLevel } from '@theia/telemetry/lib/common/telemetry-types';
18
19
  import { TelemetryExtImpl } from './telemetry-ext';
19
20
 
20
21
  const expect = chai.expect;
21
22
 
22
- describe('TelemetryLogger', () => {
23
+ describe('TelemetryExtImpl', () => {
23
24
  const sender = {
24
25
  sendEventData: () => { },
25
26
  sendErrorData: () => { }
26
27
  };
27
28
 
28
- it('reflects the current telemetry enabled state', () => {
29
+ interface Expectation {
30
+ isTelemetryEnabled: boolean;
31
+ isUsageEnabled: boolean;
32
+ isErrorsEnabled: boolean;
33
+ }
34
+
35
+ const expectations: Record<TelemetryLevel, Expectation> = {
36
+ off: { isTelemetryEnabled: false, isUsageEnabled: false, isErrorsEnabled: false },
37
+ crash: { isTelemetryEnabled: false, isUsageEnabled: false, isErrorsEnabled: false },
38
+ error: { isTelemetryEnabled: false, isUsageEnabled: false, isErrorsEnabled: true },
39
+ all: { isTelemetryEnabled: true, isUsageEnabled: true, isErrorsEnabled: true }
40
+ };
41
+
42
+ for (const level of ['off', 'crash', 'error', 'all'] as const) {
43
+ const expected = expectations[level];
44
+
45
+ it(`derives the enablement states from telemetry level '${level}'`, () => {
46
+ const telemetry = new TelemetryExtImpl();
47
+ telemetry.$onDidChangeTelemetryLevel(level);
48
+ const logger = telemetry.createTelemetryLogger(sender);
49
+
50
+ expect(telemetry.level).to.equal(level);
51
+ expect(telemetry.isTelemetryEnabled).to.equal(expected.isTelemetryEnabled);
52
+ expect(logger.isUsageEnabled).to.equal(expected.isUsageEnabled);
53
+ expect(logger.isErrorsEnabled).to.equal(expected.isErrorsEnabled);
54
+ });
55
+
56
+ it(`applies telemetry level '${level}' to loggers created before the level arrived`, () => {
57
+ const telemetry = new TelemetryExtImpl();
58
+ const logger = telemetry.createTelemetryLogger(sender);
59
+ telemetry.$onDidChangeTelemetryLevel(level);
60
+
61
+ expect(logger.isUsageEnabled).to.equal(expected.isUsageEnabled);
62
+ expect(logger.isErrorsEnabled).to.equal(expected.isErrorsEnabled);
63
+ });
64
+ }
65
+
66
+ it('only fires onDidChangeTelemetryEnabled when the boolean it reports changes', () => {
67
+ const telemetry = new TelemetryExtImpl();
68
+ const fired: boolean[] = [];
69
+ telemetry.onDidChangeTelemetryEnabled(enabled => fired.push(enabled));
70
+
71
+ telemetry.$onDidChangeTelemetryLevel('crash');
72
+ telemetry.$onDidChangeTelemetryLevel('error');
73
+ telemetry.$onDidChangeTelemetryLevel('all');
74
+ telemetry.$onDidChangeTelemetryLevel('off');
75
+
76
+ expect(fired).to.deep.equal([true, false]);
77
+ });
78
+
79
+ it('fires onDidChangeEnableStates once per transition that changes an enablement state', () => {
29
80
  const telemetry = new TelemetryExtImpl();
30
81
  const logger = telemetry.createTelemetryLogger(sender);
82
+ let changes = 0;
83
+ logger.onDidChangeEnableStates(() => changes++);
31
84
 
32
- expect(logger.telemetryEnabled).to.equal(false);
33
- expect(logger.isUsageEnabled).to.equal(false);
34
- expect(logger.isErrorsEnabled).to.equal(false);
85
+ telemetry.$onDidChangeTelemetryLevel('crash'); // off -> crash: both stay false
86
+ expect(changes).to.equal(0);
35
87
 
36
- telemetry.isTelemetryEnabled = true;
88
+ telemetry.$onDidChangeTelemetryLevel('error'); // errors become enabled
89
+ expect(changes).to.equal(1);
37
90
 
38
- expect(logger.telemetryEnabled).to.equal(true);
39
- expect(logger.isUsageEnabled).to.equal(true);
40
- expect(logger.isErrorsEnabled).to.equal(true);
91
+ telemetry.$onDidChangeTelemetryLevel('all'); // usage becomes enabled
92
+ expect(changes).to.equal(2);
41
93
 
42
- telemetry.isTelemetryEnabled = false;
94
+ telemetry.$onDidChangeTelemetryLevel('off');
95
+ expect(changes).to.equal(3);
96
+ });
43
97
 
44
- expect(logger.telemetryEnabled).to.equal(false);
98
+ it('stops following the level once the logger is disposed', () => {
99
+ const telemetry = new TelemetryExtImpl();
100
+ const logger = telemetry.createTelemetryLogger(sender);
101
+ let changes = 0;
102
+ logger.onDidChangeEnableStates(() => changes++);
103
+
104
+ logger.dispose();
105
+ telemetry.$onDidChangeTelemetryLevel('all');
45
106
  expect(logger.isUsageEnabled).to.equal(false);
46
- expect(logger.isErrorsEnabled).to.equal(false);
107
+
108
+ logger.isUsageEnabled = true;
109
+ expect(changes).to.equal(0);
110
+ });
111
+
112
+ it('logs rather than propagates a sender that fails to flush on disposal', async () => {
113
+ const telemetry = new TelemetryExtImpl();
114
+ const logged: unknown[] = [];
115
+ const consoleError = console.error;
116
+ console.error = (...args: unknown[]) => { logged.push(args); };
117
+ try {
118
+ telemetry.createTelemetryLogger({ ...sender, flush: () => { throw new Error('boom'); } }).dispose();
119
+ telemetry.createTelemetryLogger({ ...sender, flush: () => Promise.reject(new Error('boom')) }).dispose();
120
+ await new Promise(resolve => setTimeout(resolve, 0));
121
+ } finally {
122
+ console.error = consoleError;
123
+ }
124
+
125
+ expect(logged).to.have.lengthOf(2);
47
126
  });
48
127
 
49
- it('emits enable-state changes when telemetry is toggled', () => {
128
+ it('ignores a repeated telemetry level', () => {
50
129
  const telemetry = new TelemetryExtImpl();
51
130
  const logger = telemetry.createTelemetryLogger(sender);
52
131
  let changes = 0;
53
-
54
132
  logger.onDidChangeEnableStates(() => changes++);
55
133
 
56
- telemetry.isTelemetryEnabled = true;
57
- telemetry.isTelemetryEnabled = false;
134
+ telemetry.$onDidChangeTelemetryLevel('all');
135
+ telemetry.$onDidChangeTelemetryLevel('all');
58
136
 
59
- expect(changes).to.equal(2);
137
+ expect(changes).to.equal(1);
138
+ });
139
+ });
140
+
141
+ describe('TelemetryLogger', () => {
142
+
143
+ interface RecordingSender {
144
+ events: string[];
145
+ errors: Error[];
146
+ sendEventData(eventName: string): void;
147
+ sendErrorData(error: Error): void;
148
+ }
149
+
150
+ function createSender(): RecordingSender {
151
+ const events: string[] = [];
152
+ const errors: Error[] = [];
153
+ return {
154
+ events,
155
+ errors,
156
+ sendEventData: (eventName: string) => events.push(eventName),
157
+ sendErrorData: (error: Error) => errors.push(error)
158
+ };
159
+ }
160
+
161
+ it('delivers errors but suppresses usage at telemetry level \'error\'', () => {
162
+ const telemetry = new TelemetryExtImpl();
163
+ telemetry.$onDidChangeTelemetryLevel('error');
164
+ const sender = createSender();
165
+ const logger = telemetry.createTelemetryLogger(sender);
166
+
167
+ logger.logUsage('usage-event');
168
+ logger.logError('error-event');
169
+ logger.logError(new Error('boom'));
170
+
171
+ expect(sender.events).to.deep.equal(['error-event']);
172
+ expect(sender.errors).to.have.lengthOf(1);
173
+ });
174
+
175
+ it('suppresses usage that a plugin re-enables while the telemetry level forbids it', () => {
176
+ const telemetry = new TelemetryExtImpl();
177
+ telemetry.$onDidChangeTelemetryLevel('off');
178
+ const sender = createSender();
179
+ const logger = telemetry.createTelemetryLogger(sender);
180
+
181
+ logger.isUsageEnabled = true;
182
+ logger.isErrorsEnabled = true;
183
+ logger.logUsage('usage-event');
184
+ logger.logError('error-event');
185
+
186
+ expect(sender.events).to.be.empty;
187
+ });
188
+
189
+ it('refuses to let a plugin assign the telemetry level of its logger', () => {
190
+ const telemetry = new TelemetryExtImpl();
191
+ telemetry.$onDidChangeTelemetryLevel('off');
192
+ const sender = createSender();
193
+ const logger = telemetry.createTelemetryLogger(sender);
194
+
195
+ expect(Reflect.set(logger, 'level', 'all')).to.be.false;
196
+ logger.logUsage('usage-event');
197
+
198
+ expect(logger.level).to.equal('off');
199
+ expect(logger.isUsageEnabled).to.be.false;
200
+ expect(sender.events).to.be.empty;
60
201
  });
61
202
  });
@@ -14,33 +14,54 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
 
16
16
  import { Event, Emitter } from '@theia/core/lib/common/event';
17
+ import { Disposable } from '@theia/core/lib/common/disposable';
17
18
  import { cloneAndChange } from '@theia/core';
19
+ import { injectable } from '@theia/core/shared/inversify';
20
+ import { TelemetryLevel, isKindAllowedByLevel } from '@theia/telemetry/lib/common/telemetry-types';
21
+ import { TelemetryExt } from '../common/plugin-api-rpc';
18
22
  import { mixin } from '../common/types';
19
23
  import { TelemetryTrustedValue, TelemetryLoggerOptions } from './types-impl';
20
24
 
21
- export class TelemetryExtImpl {
25
+ /**
26
+ * Holds the telemetry level the user consented to and hands it to the loggers plugins create.
27
+ *
28
+ * @experimental
29
+ */
30
+ @injectable()
31
+ export class TelemetryExtImpl implements TelemetryExt {
22
32
 
23
- _isTelemetryEnabled: boolean = false; // telemetry not activated by default
33
+ protected currentLevel: TelemetryLevel = 'off';
24
34
  private readonly onDidChangeTelemetryEnabledEmitter = new Emitter<boolean>();
25
35
  readonly onDidChangeTelemetryEnabled: Event<boolean> = this.onDidChangeTelemetryEnabledEmitter.event;
36
+ private readonly onDidChangeTelemetryLevelEmitter = new Emitter<TelemetryLevel>();
37
+ protected readonly onDidChangeTelemetryLevel: Event<TelemetryLevel> = this.onDidChangeTelemetryLevelEmitter.event;
26
38
 
27
- get isTelemetryEnabled(): boolean {
28
- return this._isTelemetryEnabled;
39
+ get level(): TelemetryLevel {
40
+ return this.currentLevel;
29
41
  }
30
42
 
31
- set isTelemetryEnabled(isTelemetryEnabled: boolean) {
32
- if (this._isTelemetryEnabled !== isTelemetryEnabled) {
33
- this._isTelemetryEnabled = isTelemetryEnabled;
34
- this.onDidChangeTelemetryEnabledEmitter.fire(this._isTelemetryEnabled);
43
+ $onDidChangeTelemetryLevel(level: TelemetryLevel): void {
44
+ this.setLevel(level);
45
+ }
46
+
47
+ setLevel(level: TelemetryLevel): void {
48
+ if (this.currentLevel === level) {
49
+ return;
35
50
  }
51
+ const wasTelemetryEnabled = this.isTelemetryEnabled;
52
+ this.currentLevel = level;
53
+ this.onDidChangeTelemetryLevelEmitter.fire(level);
54
+ if (this.isTelemetryEnabled !== wasTelemetryEnabled) {
55
+ this.onDidChangeTelemetryEnabledEmitter.fire(this.isTelemetryEnabled);
56
+ }
57
+ }
58
+
59
+ get isTelemetryEnabled(): boolean {
60
+ return isKindAllowedByLevel(this.currentLevel, 'usage');
36
61
  }
37
62
 
38
63
  createTelemetryLogger(sender: TelemetrySender, options?: TelemetryLoggerOptions | undefined): TelemetryLogger {
39
- const logger = new TelemetryLogger(sender, this._isTelemetryEnabled, options);
40
- this.onDidChangeTelemetryEnabled(isEnabled => {
41
- logger.telemetryEnabled = isEnabled;
42
- });
43
- return logger;
64
+ return new TelemetryLogger(sender, this.currentLevel, this.onDidChangeTelemetryLevel, options);
44
65
  }
45
66
  }
46
67
 
@@ -49,30 +70,36 @@ export class TelemetryLogger {
49
70
  readonly options: TelemetryLoggerOptions | undefined;
50
71
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
72
  readonly commonProperties: Record<string, any>;
52
- private _telemetryEnabled: boolean;
73
+ private _level: TelemetryLevel;
53
74
 
54
75
  private readonly onDidChangeEnableStatesEmitter: Emitter<TelemetryLogger> = new Emitter();
55
76
  readonly onDidChangeEnableStates: Event<TelemetryLogger> = this.onDidChangeEnableStatesEmitter.event;
56
77
  private _isUsageEnabled: boolean;
57
78
  private _isErrorsEnabled: boolean;
79
+ private readonly levelSubscription: Disposable;
58
80
 
59
- constructor(sender: TelemetrySender, telemetryEnabled: boolean, options?: TelemetryLoggerOptions) {
81
+ constructor(sender: TelemetrySender, level: TelemetryLevel, onDidChangeTelemetryLevel: Event<TelemetryLevel>, options?: TelemetryLoggerOptions) {
60
82
  this.sender = sender;
61
83
  this.options = options;
62
84
  this.commonProperties = this.getCommonProperties();
63
- this._telemetryEnabled = telemetryEnabled;
64
- this._isErrorsEnabled = telemetryEnabled;
65
- this._isUsageEnabled = telemetryEnabled;
85
+ this._level = level;
86
+ this._isErrorsEnabled = isKindAllowedByLevel(level, 'error');
87
+ this._isUsageEnabled = isKindAllowedByLevel(level, 'usage');
88
+ this.levelSubscription = onDidChangeTelemetryLevel(next => this.applyLevel(next));
66
89
  }
67
90
 
68
- get telemetryEnabled(): boolean {
69
- return this._telemetryEnabled;
91
+ /**
92
+ * The telemetry level the user consented to. Must stay read-only: the object handed to a plugin is proxied with a
93
+ * `get` trap only, so a setter here would let the plugin raise its own gate.
94
+ */
95
+ get level(): TelemetryLevel {
96
+ return this._level;
70
97
  }
71
98
 
72
- set telemetryEnabled(telemetryEnabled: boolean) {
73
- if (this._telemetryEnabled !== telemetryEnabled) {
74
- this._telemetryEnabled = telemetryEnabled;
75
- this.updateEnableStates(telemetryEnabled, telemetryEnabled);
99
+ private applyLevel(level: TelemetryLevel): void {
100
+ if (this._level !== level) {
101
+ this._level = level;
102
+ this.updateEnableStates(isKindAllowedByLevel(level, 'usage'), isKindAllowedByLevel(level, 'error'));
76
103
  }
77
104
  }
78
105
 
@@ -108,7 +135,7 @@ export class TelemetryLogger {
108
135
 
109
136
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
137
  logUsage(eventName: string, data?: Record<string, any | TelemetryTrustedValue<any>>): void {
111
- if (!this.telemetryEnabled || !this.isUsageEnabled) {
138
+ if (!isKindAllowedByLevel(this._level, 'usage') || !this.isUsageEnabled) {
112
139
  return;
113
140
  }
114
141
  this.logEvent(eventName, data);
@@ -116,7 +143,7 @@ export class TelemetryLogger {
116
143
 
117
144
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
118
145
  logError(eventNameOrException: string | Error, data?: Record<string, any | TelemetryTrustedValue<any>>): void {
119
- if (!this.telemetryEnabled || !this.isErrorsEnabled || !this.sender) {
146
+ if (!isKindAllowedByLevel(this._level, 'error') || !this.isErrorsEnabled || !this.sender) {
120
147
  // no sender available or error shall not be sent
121
148
  return;
122
149
  }
@@ -128,14 +155,17 @@ export class TelemetryLogger {
128
155
  }
129
156
 
130
157
  dispose(): void {
131
- if (this.sender?.flush) {
132
- let tempSender: TelemetrySender | undefined = this.sender;
133
- this.sender = undefined;
134
- Promise.resolve(tempSender.flush!()).then(tempSender = undefined);
135
- } else {
136
- this.sender = undefined;
158
+ this.levelSubscription.dispose();
159
+ this.onDidChangeEnableStatesEmitter.dispose();
160
+ const sender = this.sender;
161
+ this.sender = undefined;
162
+ const reportFlushFailure = (error: unknown): void => console.error('Failed to flush the telemetry sender.', error);
163
+ try {
164
+ Promise.resolve(sender?.flush?.()).catch(reportFlushFailure);
165
+ } catch (error) {
166
+ reportFlushFailure(error);
137
167
  }
138
- };
168
+ }
139
169
 
140
170
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
171
  private logEvent(eventName: string, data?: Record<string, any>): void {
@@ -0,0 +1,34 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 Robert Jandow
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 { ContainerModule } from '@theia/core/shared/inversify';
18
+ import { HostedPluginServer, PluginServer } from './common/plugin-protocol';
19
+ import { BrowserOnlyHostedPluginServer } from './hosted/browser-only/browser-only-hosted-plugin-server';
20
+ import { PluginPathsService } from './main/common/plugin-paths-protocol';
21
+ import { BrowserOnlyPluginPathsService } from './hosted/browser-only/browser-only-plugin-paths-service';
22
+ import { BrowserOnlyPluginServer } from './hosted/browser-only/browser-only-plugin-server';
23
+ import { BrowserOnlyPluginFileServiceContribution, BrowserOnlyPluginFileSystemProvider } from './hosted/browser-only/browser-only-plugin-file-system-provider';
24
+ import { FileServiceContribution } from '@theia/filesystem/lib/browser/file-service';
25
+
26
+ export default new ContainerModule((bind, unbind, isBound, rebind) => {
27
+ rebind(HostedPluginServer).to(BrowserOnlyHostedPluginServer).inSingletonScope();
28
+ rebind(PluginServer).to(BrowserOnlyPluginServer).inSingletonScope();
29
+ rebind(PluginPathsService).to(BrowserOnlyPluginPathsService).inSingletonScope();
30
+
31
+ bind(BrowserOnlyPluginFileSystemProvider).toSelf().inSingletonScope();
32
+ bind(BrowserOnlyPluginFileServiceContribution).toSelf().inSingletonScope();
33
+ bind(FileServiceContribution).toService(BrowserOnlyPluginFileServiceContribution);
34
+ });