@theia/plugin-ext 1.76.0-next.7 → 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.
- package/lib/common/paths-util.d.ts.map +1 -1
- package/lib/common/paths-util.js +9 -2
- package/lib/common/paths-util.js.map +1 -1
- package/lib/common/paths-util.spec.d.ts +2 -0
- package/lib/common/paths-util.spec.d.ts.map +1 -0
- package/lib/common/paths-util.spec.js +42 -0
- package/lib/common/paths-util.spec.js.map +1 -0
- package/lib/common/plugin-api-rpc.d.ts +14 -0
- package/lib/common/plugin-api-rpc.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc.js +1 -1
- package/lib/common/plugin-api-rpc.js.map +1 -1
- package/lib/common/plugin-protocol.d.ts +2 -2
- package/lib/common/plugin-protocol.d.ts.map +1 -1
- package/lib/common/plugin-protocol.js +2 -1
- package/lib/common/plugin-protocol.js.map +1 -1
- package/lib/hosted/browser/hosted-plugin.d.ts +2 -0
- package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
- package/lib/hosted/browser/hosted-plugin.js +9 -3
- package/lib/hosted/browser/hosted-plugin.js.map +1 -1
- package/lib/hosted/browser/worker/worker-main.js +22 -12
- package/lib/hosted/browser/worker/worker-main.js.map +1 -1
- package/lib/hosted/browser/worker/worker-plugin-module.d.ts.map +1 -1
- package/lib/hosted/browser/worker/worker-plugin-module.js +2 -0
- package/lib/hosted/browser/worker/worker-plugin-module.js.map +1 -1
- package/lib/hosted/browser-only/browser-only-hosted-plugin-server.d.ts +37 -0
- package/lib/hosted/browser-only/browser-only-hosted-plugin-server.d.ts.map +1 -0
- package/lib/hosted/browser-only/browser-only-hosted-plugin-server.js +107 -0
- package/lib/hosted/browser-only/browser-only-hosted-plugin-server.js.map +1 -0
- package/lib/hosted/browser-only/browser-only-hosted-plugin-server.spec.d.ts +2 -0
- package/lib/hosted/browser-only/browser-only-hosted-plugin-server.spec.d.ts.map +1 -0
- package/lib/hosted/browser-only/browser-only-hosted-plugin-server.spec.js +106 -0
- package/lib/hosted/browser-only/browser-only-hosted-plugin-server.spec.js.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.d.ts +38 -0
- package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.d.ts.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.js +124 -0
- package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.js.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.spec.d.ts +2 -0
- package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.spec.d.ts.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.spec.js +137 -0
- package/lib/hosted/browser-only/browser-only-plugin-file-system-provider.spec.js.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-paths-service.d.ts +43 -0
- package/lib/hosted/browser-only/browser-only-plugin-paths-service.d.ts.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-paths-service.js +145 -0
- package/lib/hosted/browser-only/browser-only-plugin-paths-service.js.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-paths-service.spec.d.ts +2 -0
- package/lib/hosted/browser-only/browser-only-plugin-paths-service.spec.d.ts.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-paths-service.spec.js +165 -0
- package/lib/hosted/browser-only/browser-only-plugin-paths-service.spec.js.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-server.d.ts +54 -0
- package/lib/hosted/browser-only/browser-only-plugin-server.d.ts.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-server.js +155 -0
- package/lib/hosted/browser-only/browser-only-plugin-server.js.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-server.spec.d.ts +2 -0
- package/lib/hosted/browser-only/browser-only-plugin-server.spec.d.ts.map +1 -0
- package/lib/hosted/browser-only/browser-only-plugin-server.spec.js +190 -0
- package/lib/hosted/browser-only/browser-only-plugin-server.spec.js.map +1 -0
- package/lib/hosted/browser-only/test/navigator-locks-test-util.d.ts +24 -0
- package/lib/hosted/browser-only/test/navigator-locks-test-util.d.ts.map +1 -0
- package/lib/hosted/browser-only/test/navigator-locks-test-util.js +46 -0
- package/lib/hosted/browser-only/test/navigator-locks-test-util.js.map +1 -0
- package/lib/hosted/common/hosted-plugin.d.ts +2 -2
- package/lib/hosted/common/hosted-plugin.d.ts.map +1 -1
- package/lib/hosted/common/hosted-plugin.js +2 -2
- package/lib/hosted/common/hosted-plugin.js.map +1 -1
- package/lib/hosted/node/plugin-host-module.d.ts.map +1 -1
- package/lib/hosted/node/plugin-host-module.js +2 -0
- package/lib/hosted/node/plugin-host-module.js.map +1 -1
- package/lib/hosted/node/plugin-host-rpc.d.ts +3 -0
- package/lib/hosted/node/plugin-host-rpc.d.ts.map +1 -1
- package/lib/hosted/node/plugin-host-rpc.js +9 -3
- package/lib/hosted/node/plugin-host-rpc.js.map +1 -1
- package/lib/main/browser/editors-and-documents-main.d.ts +7 -0
- package/lib/main/browser/editors-and-documents-main.d.ts.map +1 -1
- package/lib/main/browser/editors-and-documents-main.js +19 -9
- package/lib/main/browser/editors-and-documents-main.js.map +1 -1
- package/lib/main/browser/editors-and-documents-main.spec.d.ts +2 -0
- package/lib/main/browser/editors-and-documents-main.spec.d.ts.map +1 -0
- package/lib/main/browser/editors-and-documents-main.spec.js +117 -0
- package/lib/main/browser/editors-and-documents-main.spec.js.map +1 -0
- package/lib/main/browser/languages-main.d.ts +7 -0
- package/lib/main/browser/languages-main.d.ts.map +1 -1
- package/lib/main/browser/languages-main.js +31 -9
- package/lib/main/browser/languages-main.js.map +1 -1
- package/lib/main/browser/languages-main.spec.d.ts +2 -0
- package/lib/main/browser/languages-main.spec.d.ts.map +1 -0
- package/lib/main/browser/languages-main.spec.js +82 -0
- package/lib/main/browser/languages-main.spec.js.map +1 -0
- package/lib/main/browser/main-context.d.ts.map +1 -1
- package/lib/main/browser/main-context.js +3 -0
- package/lib/main/browser/main-context.js.map +1 -1
- package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +5 -0
- package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
- package/lib/main/browser/menus/vscode-theia-menu-mappings.js +5 -0
- package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
- package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
- package/lib/main/browser/plugin-ext-frontend-module.js +1 -0
- package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
- package/lib/main/browser/plugin-icon-service.d.ts +6 -0
- package/lib/main/browser/plugin-icon-service.d.ts.map +1 -1
- package/lib/main/browser/plugin-icon-service.js +14 -2
- package/lib/main/browser/plugin-icon-service.js.map +1 -1
- package/lib/main/browser/plugin-icon-service.spec.d.ts +2 -0
- package/lib/main/browser/plugin-icon-service.spec.d.ts.map +1 -0
- package/lib/main/browser/plugin-icon-service.spec.js +83 -0
- package/lib/main/browser/plugin-icon-service.spec.js.map +1 -0
- package/lib/main/browser/plugin-icon-theme-service.d.ts.map +1 -1
- package/lib/main/browser/plugin-icon-theme-service.js +2 -1
- package/lib/main/browser/plugin-icon-theme-service.js.map +1 -1
- package/lib/main/browser/plugin-icon-theme-service.spec.d.ts +2 -0
- package/lib/main/browser/plugin-icon-theme-service.spec.d.ts.map +1 -0
- package/lib/main/browser/plugin-icon-theme-service.spec.js +91 -0
- package/lib/main/browser/plugin-icon-theme-service.spec.js.map +1 -0
- package/lib/main/browser/telemetry-main.d.ts +20 -0
- package/lib/main/browser/telemetry-main.d.ts.map +1 -0
- package/lib/main/browser/telemetry-main.js +43 -0
- package/lib/main/browser/telemetry-main.js.map +1 -0
- package/lib/main/browser/telemetry-main.spec.d.ts +2 -0
- package/lib/main/browser/telemetry-main.spec.d.ts.map +1 -0
- package/lib/main/browser/telemetry-main.spec.js +66 -0
- package/lib/main/browser/telemetry-main.spec.js.map +1 -0
- package/lib/main/browser/webview/webview-context-keys.d.ts +3 -2
- package/lib/main/browser/webview/webview-context-keys.d.ts.map +1 -1
- package/lib/main/browser/webview/webview-context-keys.js +16 -2
- package/lib/main/browser/webview/webview-context-keys.js.map +1 -1
- package/lib/main/browser/webview/webview-environment.d.ts.map +1 -1
- package/lib/main/browser/webview/webview-environment.js +2 -1
- package/lib/main/browser/webview/webview-environment.js.map +1 -1
- package/lib/main/common/paths/const.d.ts +13 -0
- package/lib/main/common/paths/const.d.ts.map +1 -0
- package/lib/main/common/paths/const.js +32 -0
- package/lib/main/common/paths/const.js.map +1 -0
- package/lib/main/node/paths/const.d.ts +5 -5
- package/lib/main/node/paths/const.d.ts.map +1 -1
- package/lib/main/node/paths/const.js +6 -6
- package/lib/main/node/paths/const.js.map +1 -1
- package/lib/main/node/paths/plugin-paths-service.js +1 -1
- package/lib/main/node/paths/plugin-paths-service.js.map +1 -1
- package/lib/main/node/plugin-cli-contribution.d.ts +2 -2
- package/lib/main/node/plugin-cli-contribution.d.ts.map +1 -1
- package/lib/main/node/plugin-cli-contribution.js +4 -3
- package/lib/main/node/plugin-cli-contribution.js.map +1 -1
- package/lib/main/node/plugins-key-value-storage.js +1 -1
- package/lib/main/node/plugins-key-value-storage.js.map +1 -1
- package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.d.ts +5 -0
- package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.d.ts.map +1 -1
- package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.js +12 -1
- package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.js.map +1 -1
- package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.d.ts +2 -0
- package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.d.ts.map +1 -0
- package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.js +54 -0
- package/lib/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.js.map +1 -0
- package/lib/plugin/plugin-context.d.ts +2 -1
- package/lib/plugin/plugin-context.d.ts.map +1 -1
- package/lib/plugin/plugin-context.js +2 -3
- package/lib/plugin/plugin-context.js.map +1 -1
- package/lib/plugin/plugin-manager.d.ts +2 -0
- package/lib/plugin/plugin-manager.d.ts.map +1 -1
- package/lib/plugin/plugin-manager.js +12 -1
- package/lib/plugin/plugin-manager.js.map +1 -1
- package/lib/plugin/relative-pattern.spec.d.ts +2 -0
- package/lib/plugin/relative-pattern.spec.d.ts.map +1 -0
- package/lib/plugin/relative-pattern.spec.js +51 -0
- package/lib/plugin/relative-pattern.spec.js.map +1 -0
- package/lib/plugin/telemetry-ext.d.ts +23 -7
- package/lib/plugin/telemetry-ext.d.ts.map +1 -1
- package/lib/plugin/telemetry-ext.js +60 -33
- package/lib/plugin/telemetry-ext.js.map +1 -1
- package/lib/plugin/telemetry-ext.spec.js +121 -16
- package/lib/plugin/telemetry-ext.spec.js.map +1 -1
- package/lib/plugin-ext-frontend-only-module.d.ts +4 -0
- package/lib/plugin-ext-frontend-only-module.d.ts.map +1 -0
- package/lib/plugin-ext-frontend-only-module.js +34 -0
- package/lib/plugin-ext-frontend-only-module.js.map +1 -0
- package/package.json +36 -32
- package/src/common/paths-util.spec.ts +47 -0
- package/src/common/paths-util.ts +11 -2
- package/src/common/plugin-api-rpc.ts +15 -1
- package/src/common/plugin-protocol.ts +2 -0
- package/src/hosted/browser/hosted-plugin.ts +9 -4
- package/src/hosted/browser/worker/worker-main.ts +23 -12
- package/src/hosted/browser/worker/worker-plugin-module.ts +2 -0
- package/src/hosted/browser-only/browser-only-hosted-plugin-server.spec.ts +133 -0
- package/src/hosted/browser-only/browser-only-hosted-plugin-server.ts +122 -0
- package/src/hosted/browser-only/browser-only-plugin-file-system-provider.spec.ts +178 -0
- package/src/hosted/browser-only/browser-only-plugin-file-system-provider.ts +129 -0
- package/src/hosted/browser-only/browser-only-plugin-paths-service.spec.ts +199 -0
- package/src/hosted/browser-only/browser-only-plugin-paths-service.ts +145 -0
- package/src/hosted/browser-only/browser-only-plugin-server.spec.ts +228 -0
- package/src/hosted/browser-only/browser-only-plugin-server.ts +165 -0
- package/src/hosted/browser-only/test/navigator-locks-test-util.ts +54 -0
- package/src/hosted/common/hosted-plugin.ts +4 -4
- package/src/hosted/node/plugin-host-module.ts +2 -0
- package/src/hosted/node/plugin-host-rpc.ts +10 -4
- package/src/main/browser/editors-and-documents-main.spec.ts +140 -0
- package/src/main/browser/editors-and-documents-main.ts +19 -7
- package/src/main/browser/languages-main.spec.ts +101 -0
- package/src/main/browser/languages-main.ts +31 -9
- package/src/main/browser/main-context.ts +4 -0
- package/src/main/browser/menus/vscode-theia-menu-mappings.ts +5 -0
- package/src/main/browser/plugin-ext-frontend-module.ts +2 -1
- package/src/main/browser/plugin-icon-service.spec.ts +101 -0
- package/src/main/browser/plugin-icon-service.ts +16 -3
- package/src/main/browser/plugin-icon-theme-service.spec.ts +103 -0
- package/src/main/browser/plugin-icon-theme-service.ts +2 -1
- package/src/main/browser/telemetry-main.spec.ts +77 -0
- package/src/main/browser/telemetry-main.ts +46 -0
- package/src/main/browser/webview/webview-context-keys.ts +20 -4
- package/src/main/browser/webview/webview-environment.ts +2 -1
- package/src/main/common/paths/const.ts +28 -0
- package/src/main/node/paths/const.ts +5 -5
- package/src/main/node/paths/plugin-paths-service.ts +1 -1
- package/src/main/node/plugin-cli-contribution.ts +4 -3
- package/src/main/node/plugins-key-value-storage.ts +1 -1
- package/src/main/node/resolvers/local-directory-plugin-deployer-resolver.spec.ts +61 -0
- package/src/main/node/resolvers/local-directory-plugin-deployer-resolver.ts +14 -1
- package/src/plugin/plugin-context.ts +3 -2
- package/src/plugin/plugin-manager.ts +11 -1
- package/src/plugin/relative-pattern.spec.ts +55 -0
- package/src/plugin/telemetry-ext.spec.ts +158 -17
- package/src/plugin/telemetry-ext.ts +63 -33
- package/src/plugin-ext-frontend-only-module.ts +34 -0
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
} from '@theia/plugin-utils/lib/common/plugin-model';
|
|
31
31
|
import {
|
|
32
32
|
PLUGIN_HOST_BACKEND,
|
|
33
|
+
PLUGIN_HOST_FRONTEND,
|
|
33
34
|
PluginEntryPoint,
|
|
34
35
|
PluginLifecycle,
|
|
35
36
|
PluginManifest,
|
|
@@ -116,6 +117,7 @@ export { getPluginId };
|
|
|
116
117
|
export { toPluginUrlFromUtils as toPluginUrl };
|
|
117
118
|
export {
|
|
118
119
|
PLUGIN_HOST_BACKEND,
|
|
120
|
+
PLUGIN_HOST_FRONTEND,
|
|
119
121
|
PluginEntryPoint,
|
|
120
122
|
PluginLifecycle,
|
|
121
123
|
PluginManifest,
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import { generateUuid } from '@theia/core/lib/common/uuid';
|
|
25
25
|
import { injectable, inject, postConstruct, named } from '@theia/core/shared/inversify';
|
|
26
26
|
import { PluginWorker } from './plugin-worker';
|
|
27
|
-
import { getPluginId, DeployedPlugin, HostedPluginServer } from '../../common/plugin-protocol';
|
|
27
|
+
import { getPluginId, DeployedPlugin, HostedPluginServer, PLUGIN_HOST_FRONTEND } from '../../common/plugin-protocol';
|
|
28
28
|
import { HostedPluginWatcher } from './hosted-plugin-watcher';
|
|
29
29
|
import { ExtensionKind, MAIN_RPC_CONTEXT, PluginManagerExt, UIKind } from '../../common/plugin-api-rpc';
|
|
30
30
|
import { setUpPluginApi } from '../../main/browser/main-context';
|
|
@@ -73,6 +73,7 @@ import {
|
|
|
73
73
|
} from '../common/hosted-plugin';
|
|
74
74
|
import { isRemote } from '@theia/core/lib/browser/browser';
|
|
75
75
|
import { WorkspaceTrustService } from '@theia/workspace/lib/browser/workspace-trust-service';
|
|
76
|
+
import { TelemetryConsentProvider } from '@theia/telemetry/lib/common/telemetry-consent-provider';
|
|
76
77
|
|
|
77
78
|
export type DebugActivationEvent = 'onDebugResolve' | 'onDebugInitialConfigurations' | 'onDebugAdapterProtocolTracker' | 'onDebugDynamicConfigurations';
|
|
78
79
|
|
|
@@ -183,6 +184,9 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport<PluginManag
|
|
|
183
184
|
@inject(WorkspaceTrustService)
|
|
184
185
|
protected readonly workspaceTrustService: WorkspaceTrustService;
|
|
185
186
|
|
|
187
|
+
@inject(TelemetryConsentProvider)
|
|
188
|
+
protected readonly telemetryConsentProvider: TelemetryConsentProvider;
|
|
189
|
+
|
|
186
190
|
@inject(ILogger) @named('plugin-ext:HostedPluginSupport')
|
|
187
191
|
protected override readonly logger: ILogger;
|
|
188
192
|
|
|
@@ -245,7 +249,7 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport<PluginManag
|
|
|
245
249
|
}
|
|
246
250
|
|
|
247
251
|
protected createTheiaReadyPromise(): Promise<unknown> {
|
|
248
|
-
return Promise.all([this.preferenceServiceImpl.ready, this.workspaceService.roots]);
|
|
252
|
+
return Promise.all([this.preferenceServiceImpl.ready, this.workspaceService.roots, this.telemetryConsentProvider.ready]);
|
|
249
253
|
}
|
|
250
254
|
|
|
251
255
|
protected override runOperation(operation: () => Promise<void>): Promise<void> {
|
|
@@ -364,7 +368,8 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport<PluginManag
|
|
|
364
368
|
},
|
|
365
369
|
jsonValidation,
|
|
366
370
|
pluginKind: isRemote ? ExtensionKind.Workspace : ExtensionKind.UI,
|
|
367
|
-
supportedActivationEvents
|
|
371
|
+
supportedActivationEvents,
|
|
372
|
+
telemetryLevel: this.telemetryConsentProvider.level
|
|
368
373
|
});
|
|
369
374
|
if (toDisconnect.disposed) {
|
|
370
375
|
return undefined;
|
|
@@ -375,7 +380,7 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport<PluginManag
|
|
|
375
380
|
}
|
|
376
381
|
|
|
377
382
|
protected initRpc(host: PluginHost, pluginId: string): RPCProtocol {
|
|
378
|
-
const rpc = host ===
|
|
383
|
+
const rpc = host === PLUGIN_HOST_FRONTEND ? new PluginWorker().rpc : this.createServerRpc(host);
|
|
379
384
|
setUpPluginApi(rpc, this.container);
|
|
380
385
|
this.mainPluginApiProviders.getContributions().forEach(p => p.initialize(rpc, this.container));
|
|
381
386
|
return rpc;
|
|
@@ -34,6 +34,7 @@ import { loadManifest } from './plugin-manifest-loader';
|
|
|
34
34
|
import { EnvExtImpl } from '../../../plugin/env';
|
|
35
35
|
import { DebugExtImpl } from '../../../plugin/debug/debug-ext';
|
|
36
36
|
import { LocalizationExtImpl } from '../../../plugin/localization-ext';
|
|
37
|
+
import { TelemetryExtImpl } from '../../../plugin/telemetry-ext';
|
|
37
38
|
import pluginHostModule from './worker-plugin-module';
|
|
38
39
|
import { PLUGINS_BASE_PATH } from '@theia/plugin-utils/lib/common/constants';
|
|
39
40
|
|
|
@@ -116,20 +117,28 @@ pluginManager.setPluginHost({
|
|
|
116
117
|
pluginsModulesNames.set(plugin.lifecycle.frontendModuleName!, plugin);
|
|
117
118
|
return { target: result, plugin };
|
|
118
119
|
} else {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
const common = {
|
|
121
|
+
pluginPath: pluginModel.entryPoint.backend,
|
|
122
|
+
pluginFolder: pluginModel.packagePath,
|
|
123
|
+
pluginUri: pluginModel.packageUri,
|
|
124
|
+
model: pluginModel,
|
|
125
|
+
lifecycle: pluginLifecycle,
|
|
126
|
+
isUnderDevelopment: !!plg.isUnderDevelopment
|
|
127
|
+
};
|
|
128
|
+
const plugin: Plugin = pluginModel.entryPoint.backend
|
|
129
|
+
// Runs in another host, so its manifest is that host's problem.
|
|
130
|
+
? {
|
|
131
|
+
...common,
|
|
127
132
|
get rawModel(): never {
|
|
128
133
|
throw new Error('not supported');
|
|
129
|
-
}
|
|
130
|
-
isUnderDevelopment: !!plg.isUnderDevelopment
|
|
134
|
+
}
|
|
131
135
|
}
|
|
132
|
-
|
|
136
|
+
// No entry point anywhere, so there is nothing to run - it only contributes
|
|
137
|
+
// grammars, themes and the like. It does still turn up in `theia.extensions`,
|
|
138
|
+
// where reading `packageJSON` must not throw, so give it a real manifest.
|
|
139
|
+
// Only browser-only gets here; with a backend these go to the backend host.
|
|
140
|
+
: { ...common, rawModel: await loadManifest(pluginModel) };
|
|
141
|
+
return { target: foreign, plugin };
|
|
133
142
|
}
|
|
134
143
|
}));
|
|
135
144
|
// Collect the ordered plugins and insert them in the target array:
|
|
@@ -162,6 +171,7 @@ const messageRegistryExt = container.get(MessageRegistryExt);
|
|
|
162
171
|
const clipboardExt = container.get(ClipboardExt);
|
|
163
172
|
const webviewExt = container.get(WebviewsExtImpl);
|
|
164
173
|
const localizationExt = container.get(LocalizationExtImpl);
|
|
174
|
+
const telemetryExt = container.get(TelemetryExtImpl);
|
|
165
175
|
const storageProxy = container.get(KeyValueStorageProxy);
|
|
166
176
|
|
|
167
177
|
const apiFactory = createAPIFactory(
|
|
@@ -175,7 +185,8 @@ const apiFactory = createAPIFactory(
|
|
|
175
185
|
messageRegistryExt,
|
|
176
186
|
clipboardExt,
|
|
177
187
|
webviewExt,
|
|
178
|
-
localizationExt
|
|
188
|
+
localizationExt,
|
|
189
|
+
telemetryExt
|
|
179
190
|
);
|
|
180
191
|
let defaultApi: typeof theia;
|
|
181
192
|
|
|
@@ -36,6 +36,7 @@ import { WorkerEnvExtImpl } from './worker-env-ext';
|
|
|
36
36
|
import { DebugExtImpl } from '../../../plugin/debug/debug-ext';
|
|
37
37
|
import { LocalizationExtImpl } from '../../../plugin/localization-ext';
|
|
38
38
|
import { EncodingService } from '@theia/core/lib/common/encoding-service';
|
|
39
|
+
import { TelemetryExtImpl } from '../../../plugin/telemetry-ext';
|
|
39
40
|
|
|
40
41
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
42
|
const ctx = self as any;
|
|
@@ -79,4 +80,5 @@ export default new ContainerModule(bind => {
|
|
|
79
80
|
bind(WebviewsExtImpl).toSelf().inSingletonScope();
|
|
80
81
|
bind(TerminalServiceExtImpl).toSelf().inSingletonScope();
|
|
81
82
|
bind(MinimalTerminalServiceExt).toService(TerminalServiceExtImpl);
|
|
83
|
+
bind(TelemetryExtImpl).toSelf().inSingletonScope();
|
|
82
84
|
});
|
|
@@ -0,0 +1,133 @@
|
|
|
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 { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
|
18
|
+
// `BrowserOnlyHostedPluginServer` transitively imports `Endpoint` via `@theia/core/lib/browser`,
|
|
19
|
+
// which touches `document` at load time, so JSDOM is enabled before it for that import.
|
|
20
|
+
let disableJSDOM = enableJSDOM();
|
|
21
|
+
|
|
22
|
+
import { expect } from 'chai';
|
|
23
|
+
import { Container } from '@theia/core/shared/inversify';
|
|
24
|
+
import { DeployedPlugin, PluginType } from '../../common';
|
|
25
|
+
import { BrowserOnlyHostedPluginServer, BrowserOnlyPluginOptions } from './browser-only-hosted-plugin-server';
|
|
26
|
+
|
|
27
|
+
disableJSDOM();
|
|
28
|
+
|
|
29
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
30
|
+
|
|
31
|
+
function plugin(name: string, version = '1.0.0'): DeployedPlugin {
|
|
32
|
+
return {
|
|
33
|
+
type: PluginType.System,
|
|
34
|
+
metadata: {
|
|
35
|
+
host: 'main',
|
|
36
|
+
model: { publisher: 'theia', name, version } as any,
|
|
37
|
+
lifecycle: {} as any
|
|
38
|
+
}
|
|
39
|
+
} as DeployedPlugin;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('BrowserOnlyHostedPluginServer', () => {
|
|
43
|
+
|
|
44
|
+
// `fetchDeployedPlugins` builds the list URL through `Endpoint`, which reads `self.location`.
|
|
45
|
+
before(() => { disableJSDOM = enableJSDOM(); });
|
|
46
|
+
after(() => disableJSDOM());
|
|
47
|
+
|
|
48
|
+
const originalFetch = globalThis.fetch;
|
|
49
|
+
let requestedUrls: string[];
|
|
50
|
+
|
|
51
|
+
function stubFetch(respond: () => Response | Promise<Response>): void {
|
|
52
|
+
requestedUrls = [];
|
|
53
|
+
globalThis.fetch = (async (url: any) => {
|
|
54
|
+
requestedUrls.push(String(url));
|
|
55
|
+
return respond();
|
|
56
|
+
}) as typeof globalThis.fetch;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createServer(options?: BrowserOnlyPluginOptions): BrowserOnlyHostedPluginServer {
|
|
60
|
+
const container = new Container();
|
|
61
|
+
container.bind(BrowserOnlyHostedPluginServer).toSelf().inSingletonScope();
|
|
62
|
+
if (options) {
|
|
63
|
+
container.bind(BrowserOnlyPluginOptions).toConstantValue(options);
|
|
64
|
+
}
|
|
65
|
+
return container.get(BrowserOnlyHostedPluginServer);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
globalThis.fetch = originalFetch;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('serves the plugins listed by the build', async () => {
|
|
73
|
+
stubFetch(() => Response.json([plugin('a'), plugin('b')]));
|
|
74
|
+
const server = createServer();
|
|
75
|
+
|
|
76
|
+
expect(await server.getDeployedPluginIds()).to.deep.equal(['theia.a@1.0.0', 'theia.b@1.0.0']);
|
|
77
|
+
expect(requestedUrls).to.deep.equal(['http://localhost/hostedPlugin/list.json']);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('fetches the list only once', async () => {
|
|
81
|
+
stubFetch(() => Response.json([plugin('a')]));
|
|
82
|
+
const server = createServer();
|
|
83
|
+
|
|
84
|
+
await server.getDeployedPluginIds();
|
|
85
|
+
await server.getDeployedPluginIds();
|
|
86
|
+
|
|
87
|
+
expect(requestedUrls).to.have.lengthOf(1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('prefers the metadata supplied by an adopter over the list', async () => {
|
|
91
|
+
stubFetch(() => Response.json([plugin('from-list')]));
|
|
92
|
+
const server = createServer({ pluginMetadata: [plugin('from-options')] });
|
|
93
|
+
|
|
94
|
+
expect(await server.getDeployedPluginIds()).to.deep.equal(['theia.from-options@1.0.0']);
|
|
95
|
+
expect(requestedUrls).to.be.empty;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('returns only the requested plugins', async () => {
|
|
99
|
+
stubFetch(() => Response.json([plugin('a'), plugin('b'), plugin('c')]));
|
|
100
|
+
const server = createServer();
|
|
101
|
+
|
|
102
|
+
const deployed = await server.getDeployedPlugins(['theia.a@1.0.0', 'theia.c@1.0.0']);
|
|
103
|
+
expect(deployed.map(p => p.metadata.model.name)).to.deep.equal(['a', 'c']);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('reports where the list could not be loaded from', async () => {
|
|
107
|
+
stubFetch(() => new Response('', { status: 404, statusText: 'Not Found' }));
|
|
108
|
+
const server = createServer();
|
|
109
|
+
|
|
110
|
+
await server.getDeployedPluginIds().then(
|
|
111
|
+
() => expect.fail('should have rejected'),
|
|
112
|
+
error => expect(error.message).to.contain('http://localhost/hostedPlugin/list.json').and.to.contain('404'));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('fetches the list again after a failed attempt', async () => {
|
|
116
|
+
let attempt = 0;
|
|
117
|
+
stubFetch(() => ++attempt === 1 ? new Response('', { status: 503 }) : Response.json([plugin('a')]));
|
|
118
|
+
const server = createServer();
|
|
119
|
+
|
|
120
|
+
await server.getDeployedPluginIds().catch(() => undefined);
|
|
121
|
+
expect(await server.getDeployedPluginIds()).to.deep.equal(['theia.a@1.0.0']);
|
|
122
|
+
expect(requestedUrls).to.have.lengthOf(2);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('rejects a list that is not an array', async () => {
|
|
126
|
+
stubFetch(() => Response.json({ plugins: [] }));
|
|
127
|
+
const server = createServer();
|
|
128
|
+
|
|
129
|
+
await server.getDeployedPluginIds().then(
|
|
130
|
+
() => expect.fail('should have rejected'),
|
|
131
|
+
error => expect(error.message).to.contain('expected an array'));
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
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 { injectable, inject, optional } from '@theia/core/shared/inversify';
|
|
18
|
+
import { LIST_JSON, PLUGINS_BASE_PATH } from '@theia/plugin-utils/lib/common/constants';
|
|
19
|
+
import { DeployedPlugin, ExtPluginApi, HostedPluginClient, HostedPluginServer, PluginIdentifiers } from '../../common';
|
|
20
|
+
import { Event, RpcConnectionEventEmitter } from '@theia/core';
|
|
21
|
+
import { Endpoint } from '@theia/core/lib/browser';
|
|
22
|
+
|
|
23
|
+
export const BrowserOnlyPluginOptions = Symbol('BrowserOnlyPluginOptions');
|
|
24
|
+
/**
|
|
25
|
+
* Optional override for the statically deployed plugins of a browser-only application. By
|
|
26
|
+
* default we use whatever the build prepared, i.e. `lib/frontend/hostedPlugin/list.json`. Bind
|
|
27
|
+
* this to supply the metadata by hand instead, e.g. if the plugins are hosted somewhere the
|
|
28
|
+
* build can't see.
|
|
29
|
+
*/
|
|
30
|
+
export interface BrowserOnlyPluginOptions {
|
|
31
|
+
pluginMetadata: DeployedPlugin[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Serves the plugins statically deployed into the frontend bundle. There's no backend in a
|
|
36
|
+
* browser-only application, so nothing can be deployed or undeployed at runtime.
|
|
37
|
+
*/
|
|
38
|
+
@injectable()
|
|
39
|
+
export class BrowserOnlyHostedPluginServer implements HostedPluginServer, RpcConnectionEventEmitter {
|
|
40
|
+
readonly onDidOpenConnection: Event<void> = Event.None;
|
|
41
|
+
readonly onDidCloseConnection: Event<void> = Event.None;
|
|
42
|
+
|
|
43
|
+
@inject(BrowserOnlyPluginOptions) @optional()
|
|
44
|
+
protected readonly options?: BrowserOnlyPluginOptions;
|
|
45
|
+
|
|
46
|
+
protected client: HostedPluginClient | undefined;
|
|
47
|
+
|
|
48
|
+
protected plugins: Promise<DeployedPlugin[]> | undefined;
|
|
49
|
+
|
|
50
|
+
/** The statically deployed plugins, from {@link BrowserOnlyPluginOptions} if bound, otherwise from the list the build wrote. */
|
|
51
|
+
protected getPlugins(): Promise<DeployedPlugin[]> {
|
|
52
|
+
if (!this.plugins) {
|
|
53
|
+
this.plugins = this.options ? Promise.resolve(this.options.pluginMetadata) : this.fetchDeployedPlugins();
|
|
54
|
+
// don't keep a failed fetch around, otherwise the tab has no plugins until it's reloaded
|
|
55
|
+
this.plugins.catch(() => this.plugins = undefined);
|
|
56
|
+
}
|
|
57
|
+
return this.plugins;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected async fetchDeployedPlugins(): Promise<DeployedPlugin[]> {
|
|
61
|
+
// Built through `Endpoint`, like `PluginIconService#toPluginUrl`, so this keeps working
|
|
62
|
+
// when the application is deployed under a sub-path.
|
|
63
|
+
const url = new Endpoint({ path: `${PLUGINS_BASE_PATH}/${LIST_JSON}` }).getRestUrl().toString();
|
|
64
|
+
try {
|
|
65
|
+
const response = await fetch(url);
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
throw new Error(`${response.status} ${response.statusText}`);
|
|
68
|
+
}
|
|
69
|
+
const plugins = await response.json();
|
|
70
|
+
if (!Array.isArray(plugins)) {
|
|
71
|
+
throw new Error(`expected an array but got ${typeof plugins}`);
|
|
72
|
+
}
|
|
73
|
+
return plugins;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw new Error(`Failed to load the deployed plugins from '${url}': ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async getDeployedPluginIds(): Promise<PluginIdentifiers.VersionedId[]> {
|
|
80
|
+
const plugins = await this.getPlugins();
|
|
81
|
+
return plugins.map(p => PluginIdentifiers.componentsToVersionedId(p.metadata.model));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getInstalledPluginIds(): Promise<PluginIdentifiers.VersionedId[]> {
|
|
85
|
+
// the statically deployed plugins are the only ones that could ever be "installed"
|
|
86
|
+
return this.getDeployedPluginIds();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async getUninstalledPluginIds(): Promise<readonly PluginIdentifiers.VersionedId[]> {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getDisabledPluginIds(): Promise<readonly PluginIdentifiers.UnversionedId[]> {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async getDeployedPlugins(ids: PluginIdentifiers.VersionedId[]): Promise<DeployedPlugin[]> {
|
|
98
|
+
const plugins = await this.getPlugins();
|
|
99
|
+
const requested = new Set(ids);
|
|
100
|
+
return plugins.filter(p => requested.has(PluginIdentifiers.componentsToVersionedId(p.metadata.model)));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getExtPluginAPI(): Promise<ExtPluginApi[]> {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async onMessage(targetHost: string, message: Uint8Array): Promise<void> {
|
|
108
|
+
// the frontend delivers messages to the plugin host directly, not through here
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
setClient(client: HostedPluginClient | undefined): void {
|
|
112
|
+
this.client = client;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getClient(): HostedPluginClient | undefined {
|
|
116
|
+
return this.client;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
dispose(): void {
|
|
120
|
+
this.client = undefined;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
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 { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
|
18
|
+
// `BrowserOnlyPluginFileSystemProvider` transitively imports `Endpoint` via `@theia/core/lib/browser`,
|
|
19
|
+
// which touches `document` at load time, so JSDOM is enabled before it for that import.
|
|
20
|
+
let disableJSDOM = enableJSDOM();
|
|
21
|
+
|
|
22
|
+
import { expect } from 'chai';
|
|
23
|
+
import { URI } from '@theia/core';
|
|
24
|
+
import { FileSystemProviderErrorCode } from '@theia/filesystem/lib/common/files';
|
|
25
|
+
import { PLUGINS_SCHEME } from '@theia/plugin-utils/lib/common/constants';
|
|
26
|
+
import { BrowserOnlyPluginFileSystemProvider } from './browser-only-plugin-file-system-provider';
|
|
27
|
+
|
|
28
|
+
disableJSDOM();
|
|
29
|
+
|
|
30
|
+
describe('BrowserOnlyPluginFileSystemProvider', () => {
|
|
31
|
+
|
|
32
|
+
// The provider builds asset URLs through `Endpoint`, which reads `self.location`.
|
|
33
|
+
before(() => { disableJSDOM = enableJSDOM(); });
|
|
34
|
+
after(() => disableJSDOM());
|
|
35
|
+
|
|
36
|
+
const originalFetch = globalThis.fetch;
|
|
37
|
+
let requests: Array<{ url: string, method: string | undefined }>;
|
|
38
|
+
|
|
39
|
+
function stubFetch(respond: () => Response | Promise<Response>): void {
|
|
40
|
+
requests = [];
|
|
41
|
+
globalThis.fetch = (async (url: unknown, init?: RequestInit) => {
|
|
42
|
+
requests.push({ url: String(url), method: init?.method });
|
|
43
|
+
return respond();
|
|
44
|
+
}) as typeof globalThis.fetch;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function stubFetchFailure(error: Error): void {
|
|
48
|
+
requests = [];
|
|
49
|
+
globalThis.fetch = (async (url: unknown, init?: RequestInit) => {
|
|
50
|
+
requests.push({ url: String(url), method: init?.method });
|
|
51
|
+
throw error;
|
|
52
|
+
}) as typeof globalThis.fetch;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
globalThis.fetch = originalFetch;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
function assetUri(path: string): URI {
|
|
60
|
+
return new URI(`${PLUGINS_SCHEME}:/`).resolve(path);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function createProvider(): BrowserOnlyPluginFileSystemProvider {
|
|
64
|
+
return new BrowserOnlyPluginFileSystemProvider();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
it('rejects a resource whose scheme is not the plugin asset scheme', async () => {
|
|
68
|
+
const provider = createProvider();
|
|
69
|
+
|
|
70
|
+
await provider.stat(new URI('file:///theia.foo/1.0.0/package.json')).then(
|
|
71
|
+
() => expect.fail('should have rejected'),
|
|
72
|
+
error => expect(error.code).to.equal(FileSystemProviderErrorCode.FileNotFound));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('maps content-length and last-modified into size and mtime', async () => {
|
|
76
|
+
const modified = new Date('2020-01-01T00:00:00.000Z');
|
|
77
|
+
stubFetch(() => new Response(undefined, {
|
|
78
|
+
status: 200,
|
|
79
|
+
headers: { 'content-length': '42', 'last-modified': modified.toUTCString() }
|
|
80
|
+
}));
|
|
81
|
+
const provider = createProvider();
|
|
82
|
+
|
|
83
|
+
const stat = await provider.stat(assetUri('theia.foo/1.0.0/icon.svg'));
|
|
84
|
+
|
|
85
|
+
expect(stat.size).to.equal(42);
|
|
86
|
+
expect(stat.mtime).to.equal(modified.getTime());
|
|
87
|
+
expect(stat.ctime).to.equal(modified.getTime());
|
|
88
|
+
expect(requests).to.deep.equal([{ url: 'http://localhost/hostedPlugin/theia.foo/1.0.0/icon.svg', method: 'HEAD' }]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('falls back to 0 when content-length/last-modified are absent', async () => {
|
|
92
|
+
stubFetch(() => new Response(undefined, { status: 200 }));
|
|
93
|
+
const provider = createProvider();
|
|
94
|
+
|
|
95
|
+
const stat = await provider.stat(assetUri('theia.foo/1.0.0/icon.svg'));
|
|
96
|
+
|
|
97
|
+
expect(stat.size).to.equal(0);
|
|
98
|
+
expect(stat.mtime).to.equal(0);
|
|
99
|
+
expect(stat.ctime).to.equal(0);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('falls back to 0 when content-length/last-modified cannot be parsed', async () => {
|
|
103
|
+
stubFetch(() => new Response(undefined, {
|
|
104
|
+
status: 200,
|
|
105
|
+
headers: { 'content-length': 'not-a-number', 'last-modified': 'not-a-date' }
|
|
106
|
+
}));
|
|
107
|
+
const provider = createProvider();
|
|
108
|
+
|
|
109
|
+
const stat = await provider.stat(assetUri('theia.foo/1.0.0/icon.svg'));
|
|
110
|
+
|
|
111
|
+
expect(stat.size).to.equal(0);
|
|
112
|
+
expect(stat.mtime).to.equal(0);
|
|
113
|
+
expect(stat.ctime).to.equal(0);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('returns the response body as bytes', async () => {
|
|
117
|
+
const bytes = new Uint8Array([1, 2, 3, 4]);
|
|
118
|
+
stubFetch(() => new Response(bytes, { status: 200 }));
|
|
119
|
+
const provider = createProvider();
|
|
120
|
+
|
|
121
|
+
const result = await provider.readFile(assetUri('theia.foo/1.0.0/icon.svg'));
|
|
122
|
+
|
|
123
|
+
expect(Array.from(result)).to.deep.equal(Array.from(bytes));
|
|
124
|
+
expect(requests).to.deep.equal([{ url: 'http://localhost/hostedPlugin/theia.foo/1.0.0/icon.svg', method: 'GET' }]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('maps a 404 response to FileNotFound', async () => {
|
|
128
|
+
stubFetch(() => new Response('', { status: 404, statusText: 'Not Found' }));
|
|
129
|
+
const provider = createProvider();
|
|
130
|
+
|
|
131
|
+
await provider.readFile(assetUri('theia.foo/1.0.0/missing.svg')).then(
|
|
132
|
+
() => expect.fail('should have rejected'),
|
|
133
|
+
error => expect(error.code).to.equal(FileSystemProviderErrorCode.FileNotFound));
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('maps other non-ok responses to Unknown', async () => {
|
|
137
|
+
stubFetch(() => new Response('', { status: 500, statusText: 'Internal Server Error' }));
|
|
138
|
+
const provider = createProvider();
|
|
139
|
+
|
|
140
|
+
await provider.readFile(assetUri('theia.foo/1.0.0/icon.svg')).then(
|
|
141
|
+
() => expect.fail('should have rejected'),
|
|
142
|
+
error => expect(error.code).to.equal(FileSystemProviderErrorCode.Unknown));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('maps a network failure to Unavailable', async () => {
|
|
146
|
+
stubFetchFailure(new Error('network down'));
|
|
147
|
+
const provider = createProvider();
|
|
148
|
+
|
|
149
|
+
await provider.readFile(assetUri('theia.foo/1.0.0/icon.svg')).then(
|
|
150
|
+
() => expect.fail('should have rejected'),
|
|
151
|
+
error => expect(error.code).to.equal(FileSystemProviderErrorCode.Unavailable));
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('rejects mutating operations as read-only', () => {
|
|
155
|
+
const provider = createProvider();
|
|
156
|
+
const uri = assetUri('theia.foo/1.0.0/icon.svg');
|
|
157
|
+
|
|
158
|
+
expect(() => provider.mkdir(uri)).to.throw().that.has.property('code', FileSystemProviderErrorCode.NoPermissions);
|
|
159
|
+
expect(() => provider.delete(uri, { recursive: false, useTrash: false })).to.throw()
|
|
160
|
+
.that.has.property('code', FileSystemProviderErrorCode.NoPermissions);
|
|
161
|
+
expect(() => provider.rename(uri, uri, { overwrite: false })).to.throw()
|
|
162
|
+
.that.has.property('code', FileSystemProviderErrorCode.NoPermissions);
|
|
163
|
+
expect(() => provider.writeFile(uri, new Uint8Array(), { create: true, overwrite: true })).to.throw()
|
|
164
|
+
.that.has.property('code', FileSystemProviderErrorCode.NoPermissions);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('resolves the request URL through Endpoint, re-encoding # ? % and spaces while keeping / literal', async () => {
|
|
168
|
+
stubFetch(() => new Response(undefined, { status: 200 }));
|
|
169
|
+
const provider = createProvider();
|
|
170
|
+
|
|
171
|
+
await provider.stat(assetUri('theia.foo/1.0.0/a b#c?d%e.svg'));
|
|
172
|
+
|
|
173
|
+
expect(requests).to.deep.equal([{
|
|
174
|
+
url: 'http://localhost/hostedPlugin/theia.foo/1.0.0/a%20b%23c%3Fd%25e.svg',
|
|
175
|
+
method: 'HEAD'
|
|
176
|
+
}]);
|
|
177
|
+
});
|
|
178
|
+
});
|