@eclipse-glsp/server 2.7.0-next.6 → 2.7.0-next.9
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/actions/action-dispatcher.d.ts.map +1 -1
- package/lib/common/actions/action-dispatcher.js +6 -0
- package/lib/common/actions/action-dispatcher.js.map +1 -1
- package/lib/common/actions/global-action-provider.d.ts.map +1 -1
- package/lib/common/actions/global-action-provider.js +1 -1
- package/lib/common/actions/global-action-provider.js.map +1 -1
- package/lib/common/di/client-session-module.d.ts +8 -1
- package/lib/common/di/client-session-module.d.ts.map +1 -1
- package/lib/common/di/client-session-module.js +9 -1
- package/lib/common/di/client-session-module.js.map +1 -1
- package/lib/common/di/server-module.d.ts +3 -2
- package/lib/common/di/server-module.d.ts.map +1 -1
- package/lib/common/di/server-module.js +5 -1
- package/lib/common/di/server-module.js.map +1 -1
- package/lib/common/launch/jsonrpc-server-launcher.d.ts +1 -1
- package/lib/common/launch/jsonrpc-server-launcher.d.ts.map +1 -1
- package/lib/common/launch/jsonrpc-server-launcher.js +4 -1
- package/lib/common/launch/jsonrpc-server-launcher.js.map +1 -1
- package/lib/common/operations/create-operation-handler.d.ts +8 -2
- package/lib/common/operations/create-operation-handler.d.ts.map +1 -1
- package/lib/common/operations/create-operation-handler.js +16 -2
- package/lib/common/operations/create-operation-handler.js.map +1 -1
- package/lib/common/protocol/glsp-server.d.ts +7 -2
- package/lib/common/protocol/glsp-server.d.ts.map +1 -1
- package/lib/common/protocol/glsp-server.js +23 -2
- package/lib/common/protocol/glsp-server.js.map +1 -1
- package/lib/common/session/client-session-manager.d.ts +8 -1
- package/lib/common/session/client-session-manager.d.ts.map +1 -1
- package/lib/common/session/client-session-manager.js +4 -1
- package/lib/common/session/client-session-manager.js.map +1 -1
- package/package.json +3 -3
- package/src/common/actions/action-dispatcher.ts +6 -0
- package/src/common/actions/global-action-provider.ts +2 -3
- package/src/common/di/client-session-module.ts +9 -1
- package/src/common/di/server-module.ts +9 -2
- package/src/common/launch/jsonrpc-server-launcher.ts +4 -1
- package/src/common/operations/create-operation-handler.ts +16 -3
- package/src/common/protocol/glsp-server.ts +23 -1
- package/src/common/session/client-session-manager.ts +12 -1
- package/src/common/test/mock-util.ts +4 -0
- package/src/node/actions/action-dispatcher.spec.ts +39 -17
- package/lib/browser/di/browser-dispatch-context.d.ts +0 -39
- package/lib/browser/di/browser-dispatch-context.d.ts.map +0 -1
- package/lib/browser/di/browser-dispatch-context.js +0 -57
- package/lib/browser/di/browser-dispatch-context.js.map +0 -1
- package/lib/browser/di/sync-dispatch-context.d.ts +0 -39
- package/lib/browser/di/sync-dispatch-context.d.ts.map +0 -1
- package/lib/browser/di/sync-dispatch-context.js +0 -57
- package/lib/browser/di/sync-dispatch-context.js.map +0 -1
- package/lib/common/utils/action-channel.d.ts +0 -58
- package/lib/common/utils/action-channel.d.ts.map +0 -1
- package/lib/common/utils/action-channel.js +0 -99
- package/lib/common/utils/action-channel.js.map +0 -1
- package/lib/node/di/async-dispatch-context.d.ts +0 -26
- package/lib/node/di/async-dispatch-context.d.ts.map +0 -1
- package/lib/node/di/async-dispatch-context.js +0 -35
- package/lib/node/di/async-dispatch-context.js.map +0 -1
- package/lib/node/di/node-dispatch-context.d.ts +0 -26
- package/lib/node/di/node-dispatch-context.d.ts.map +0 -1
- package/lib/node/di/node-dispatch-context.js +0 -35
- package/lib/node/di/node-dispatch-context.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eclipse-glsp/server",
|
|
3
|
-
"version": "2.7.0-next.
|
|
3
|
+
"version": "2.7.0-next.9+a58ea45",
|
|
4
4
|
"description": "A js server framework for Eclipse GLSP",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eclipse",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"watch": "tsc -w"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@eclipse-glsp/graph": "2.7.0-next.
|
|
61
|
+
"@eclipse-glsp/graph": "2.7.0-next.9+a58ea45",
|
|
62
62
|
"@eclipse-glsp/protocol": "next",
|
|
63
63
|
"@types/uuid": "8.3.1",
|
|
64
64
|
"commander": "^8.3.0",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"publishConfig": {
|
|
79
79
|
"access": "public"
|
|
80
80
|
},
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "a58ea45de80be3d2fcf46e0934af141b213f6187"
|
|
82
82
|
}
|
|
@@ -219,6 +219,12 @@ export class DefaultActionDispatcher implements ActionDispatcher, Disposable {
|
|
|
219
219
|
|
|
220
220
|
const actionHandlers = this.actionHandlerRegistry.get(action.kind);
|
|
221
221
|
if (!handledOnClient && actionHandlers.length === 0) {
|
|
222
|
+
// Stale response (e.g. late `RejectAction`) for a pending request that's already
|
|
223
|
+
// gone — protocol signal, not a handler invocation; drop silently.
|
|
224
|
+
if (ResponseAction.is(action)) {
|
|
225
|
+
this.logger.debug(`Stale response '${action.kind}' (responseId='${action.responseId}') has no handler; dropping.`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
222
228
|
throw new GLSPServerError(`No handler registered for action kind: ${action.kind}`);
|
|
223
229
|
}
|
|
224
230
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
********************************************************************************/
|
|
16
16
|
import { distinctAdd } from '@eclipse-glsp/protocol';
|
|
17
17
|
import { Container, ContainerModule, inject, injectable } from 'inversify';
|
|
18
|
-
import { createClientSessionModule } from '../di/client-session-module';
|
|
18
|
+
import { TEMPORARY_CLIENT_ID, createClientSessionModule } from '../di/client-session-module';
|
|
19
19
|
import { DiagramModules, InjectionContainer } from '../di/service-identifiers';
|
|
20
20
|
import { ClientSessionInitializer } from '../session/client-session-initializer';
|
|
21
21
|
import { ActionHandlerRegistry } from './action-handler-registry';
|
|
@@ -50,8 +50,7 @@ export class DefaultGlobalActionProvider implements GlobalActionProvider {
|
|
|
50
50
|
createDiagramContainer(serverContainer: Container, modules: ContainerModule[]): Container {
|
|
51
51
|
const container = serverContainer.createChild();
|
|
52
52
|
const clientSessionModule = createClientSessionModule({
|
|
53
|
-
clientId:
|
|
54
|
-
|
|
53
|
+
clientId: TEMPORARY_CLIENT_ID,
|
|
55
54
|
glspClient: { process: () => {} },
|
|
56
55
|
clientActionKinds: []
|
|
57
56
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2022-
|
|
2
|
+
* Copyright (c) 2022-2026 STMicroelectronics and others.
|
|
3
3
|
*
|
|
4
4
|
* This program and the accompanying materials are made available under the
|
|
5
5
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
@@ -24,6 +24,14 @@ export interface ClientSessionModuleOptions {
|
|
|
24
24
|
clientActionKinds: string[];
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Synthetic `ClientId` used by framework probes that load diagram modules onto a throwaway
|
|
29
|
+
* container — e.g. {@link DefaultGlobalActionProvider} scraping action-kind metadata at server
|
|
30
|
+
* startup. Adopters can detect this id in their `ClientSessionInitializer`s to suppress
|
|
31
|
+
* setup-time diagnostics that don't apply when no real GLSP client is connected.
|
|
32
|
+
*/
|
|
33
|
+
export const TEMPORARY_CLIENT_ID = 'tempId';
|
|
34
|
+
|
|
27
35
|
/**
|
|
28
36
|
* Creates the DI module that binds client session specific configuration
|
|
29
37
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2022-
|
|
2
|
+
* Copyright (c) 2022-2026 STMicroelectronics and others.
|
|
3
3
|
*
|
|
4
4
|
* This program and the accompanying materials are made available under the
|
|
5
5
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
********************************************************************************/
|
|
16
|
-
import { GLSPServer, GLSPServerListener } from '@eclipse-glsp/protocol';
|
|
16
|
+
import { GLSPServer, GLSPServerInitializer, GLSPServerListener } from '@eclipse-glsp/protocol';
|
|
17
17
|
import { ContainerModule, interfaces } from 'inversify';
|
|
18
18
|
import { DefaultGlobalActionProvider, GlobalActionProvider } from '../actions/global-action-provider';
|
|
19
19
|
import { DefaultGLSPServer } from '../protocol/glsp-server';
|
|
@@ -77,6 +77,9 @@ export class ServerModule extends GLSPModule {
|
|
|
77
77
|
this.configureMultiBinding(new MultiBinding<GLSPServerListener>(GLSPServerListener), binding =>
|
|
78
78
|
this.configureGLSPServerListeners(binding)
|
|
79
79
|
);
|
|
80
|
+
this.configureMultiBinding(new MultiBinding<GLSPServerInitializer>(GLSPServerInitializer), binding =>
|
|
81
|
+
this.configureGLSPServerInitializers(binding)
|
|
82
|
+
);
|
|
80
83
|
|
|
81
84
|
applyBindingTarget(context, GlobalActionProvider, this.bindGlobalActionProvider()).inSingletonScope();
|
|
82
85
|
|
|
@@ -112,4 +115,8 @@ export class ServerModule extends GLSPModule {
|
|
|
112
115
|
protected configureGLSPServerListeners(binding: MultiBinding<GLSPServerListener>): void {
|
|
113
116
|
binding.add({ service: ClientSessionManager });
|
|
114
117
|
}
|
|
118
|
+
|
|
119
|
+
protected configureGLSPServerInitializers(binding: MultiBinding<GLSPServerInitializer>): void {
|
|
120
|
+
// Can be overridden to add contributions
|
|
121
|
+
}
|
|
115
122
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2023 EclipseSource and others.
|
|
2
|
+
* Copyright (c) 2023-2026 EclipseSource and others.
|
|
3
3
|
*
|
|
4
4
|
* This program and the accompanying materials are made available under the
|
|
5
5
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
@@ -83,6 +83,9 @@ export abstract class JsonRpcGLSPServerLauncher<T> extends GLSPServerLauncher<T>
|
|
|
83
83
|
serverInstance.clientConnection.onNotification(JsonrpcGLSPClient.ShutdownNotification, () =>
|
|
84
84
|
this.disposeServerInstance(serverInstance)
|
|
85
85
|
);
|
|
86
|
+
// A connection may be unceremoniously be closed (e.g., closing/reloading the browser) in which
|
|
87
|
+
// case the server must still be disposed
|
|
88
|
+
serverInstance.clientConnection.onClose(() => this.disposeServerInstance(serverInstance));
|
|
86
89
|
|
|
87
90
|
this.logger.info('Starting GLSP server connection');
|
|
88
91
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2022-
|
|
2
|
+
* Copyright (c) 2022-2026 STMicroelectronics and others.
|
|
3
3
|
*
|
|
4
4
|
* This program and the accompanying materials are made available under the
|
|
5
5
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
@@ -38,6 +38,12 @@ export interface CreateEdgeOperationHandler extends OperationHandler {
|
|
|
38
38
|
createCommand(operation: CreateEdgeOperation): MaybePromise<Command | undefined>;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export namespace CreateEdgeOperationHandler {
|
|
42
|
+
export function is(object: unknown): object is CreateEdgeOperationHandler {
|
|
43
|
+
return CreateOperationHandler.is(object) && object.operationType === CreateEdgeOperation.KIND;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
export interface CreateNodeOperationHandler extends OperationHandler {
|
|
42
48
|
readonly label: string;
|
|
43
49
|
elementTypeIds: string[];
|
|
@@ -45,14 +51,19 @@ export interface CreateNodeOperationHandler extends OperationHandler {
|
|
|
45
51
|
getTriggerActions(): TriggerNodeCreationAction[];
|
|
46
52
|
createCommand(operation: CreateNodeOperation): MaybePromise<Command | undefined>;
|
|
47
53
|
}
|
|
54
|
+
|
|
55
|
+
export namespace CreateNodeOperationHandler {
|
|
56
|
+
export function is(object: unknown): object is CreateNodeOperationHandler {
|
|
57
|
+
return CreateOperationHandler.is(object) && object.operationType === CreateNodeOperation.KIND;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
48
61
|
/**
|
|
49
62
|
* A special {@link OperationHandler} that is responsible for the handling of a node or edge creation operation. Depending on its
|
|
50
63
|
* operation type the triggered actions are {@link TriggerNodeCreationAction} or {@link TriggerEdgeCreationAction}s.
|
|
51
64
|
*/
|
|
52
65
|
export type CreateOperationHandler = CreateNodeOperationHandler | CreateEdgeOperationHandler;
|
|
53
66
|
|
|
54
|
-
export type CreateOperationKind = typeof CreateNodeOperation.KIND | typeof CreateEdgeOperation.KIND;
|
|
55
|
-
|
|
56
67
|
export namespace CreateOperationHandler {
|
|
57
68
|
export function is(object: unknown): object is CreateOperationHandler {
|
|
58
69
|
return (
|
|
@@ -64,3 +75,5 @@ export namespace CreateOperationHandler {
|
|
|
64
75
|
);
|
|
65
76
|
}
|
|
66
77
|
}
|
|
78
|
+
|
|
79
|
+
export type CreateOperationKind = typeof CreateNodeOperation.KIND | typeof CreateEdgeOperation.KIND;
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
DisposeClientSessionParameters,
|
|
20
20
|
GLSPClientProxy,
|
|
21
21
|
GLSPServer,
|
|
22
|
+
GLSPServerInitializer,
|
|
22
23
|
GLSPServerListener,
|
|
23
24
|
InitializeClientSessionParameters,
|
|
24
25
|
InitializeParameters,
|
|
@@ -63,7 +64,10 @@ export class DefaultGLSPServer implements GLSPServer {
|
|
|
63
64
|
protected clientSessions: Map<string, ClientSession>;
|
|
64
65
|
protected serverListeners: GLSPServerListener[] = [];
|
|
65
66
|
|
|
66
|
-
constructor(
|
|
67
|
+
constructor(
|
|
68
|
+
@multiInject(GLSPServerListener) @optional() serverListeners: GLSPServerListener[] = [],
|
|
69
|
+
@multiInject(GLSPServerInitializer) @optional() protected initializers: GLSPServerInitializer[] = []
|
|
70
|
+
) {
|
|
67
71
|
this.clientSessions = new Map<string, ClientSession>();
|
|
68
72
|
serverListeners.forEach(listener => this.addListener(listener));
|
|
69
73
|
}
|
|
@@ -103,12 +107,30 @@ export class DefaultGLSPServer implements GLSPServer {
|
|
|
103
107
|
|
|
104
108
|
let result = { protocolVersion: DefaultGLSPServer.PROTOCOL_VERSION, serverActions };
|
|
105
109
|
|
|
110
|
+
result = await this.initializeServer(params, result);
|
|
111
|
+
// keep for backwards compatibility
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
106
113
|
result = await this.handleInitializeArgs(result, params.args);
|
|
107
114
|
this.getListenersToNotify('serverInitialized').forEach((listener: GLSPServerListener) => listener.serverInitialized!(this));
|
|
108
115
|
this.initializeResult = result;
|
|
109
116
|
return result;
|
|
110
117
|
}
|
|
111
118
|
|
|
119
|
+
protected async initializeServer(params: InitializeParameters, result: InitializeResult): Promise<InitializeResult> {
|
|
120
|
+
for (const initializer of this.initializers) {
|
|
121
|
+
try {
|
|
122
|
+
result = await initializer.initializeServer(this, params, result);
|
|
123
|
+
} catch (error: unknown) {
|
|
124
|
+
this.logger.error(`Error during server initialization from ${initializer.constructor.name}:`, error);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @deprecated Register a `GLSPServerInitializer` instead.
|
|
133
|
+
*/
|
|
112
134
|
protected handleInitializeArgs(result: InitializeResult, args: Args | undefined): MaybePromise<InitializeResult> {
|
|
113
135
|
return result;
|
|
114
136
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2022-
|
|
2
|
+
* Copyright (c) 2022-2026 STMicroelectronics and others.
|
|
3
3
|
*
|
|
4
4
|
* This program and the accompanying materials are made available under the
|
|
5
5
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
@@ -47,6 +47,13 @@ export interface ClientSessionManager {
|
|
|
47
47
|
*/
|
|
48
48
|
getSession(clientSessionId: string): ClientSession | undefined;
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Return all currently active {@link ClientSession}s.
|
|
52
|
+
*
|
|
53
|
+
* @returns An array of all currently active {@link ClientSession}s.
|
|
54
|
+
*/
|
|
55
|
+
getSessions(): Array<ClientSession>;
|
|
56
|
+
|
|
50
57
|
/**
|
|
51
58
|
* Return all currently active {@link ClientSession}s for the given diagram type.
|
|
52
59
|
*
|
|
@@ -129,6 +136,10 @@ export class DefaultClientSessionManager implements ClientSessionManager, GLSPSe
|
|
|
129
136
|
return this.clientSessions.get(clientSessionId);
|
|
130
137
|
}
|
|
131
138
|
|
|
139
|
+
getSessions(): ClientSession[] {
|
|
140
|
+
return Array.from(this.clientSessions.values());
|
|
141
|
+
}
|
|
142
|
+
|
|
132
143
|
getSessionsByType(diagramType: string): ClientSession[] {
|
|
133
144
|
return Array.from(this.clientSessions.values()).filter(session => session.diagramType === diagramType);
|
|
134
145
|
}
|
|
@@ -169,6 +169,10 @@ export class StubClientSessionManager implements ClientSessionManager {
|
|
|
169
169
|
return undefined;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
getSessions(): ClientSession[] {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
|
|
172
176
|
getSessionsByType(diagramType: string): ClientSession[] {
|
|
173
177
|
return [];
|
|
174
178
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
********************************************************************************/
|
|
16
|
-
import { Action, Deferred, RequestAction, ResponseAction, UpdateModelAction } from '@eclipse-glsp/protocol';
|
|
16
|
+
import { Action, Deferred, RejectAction, RequestAction, ResponseAction, UpdateModelAction } from '@eclipse-glsp/protocol';
|
|
17
17
|
import { expect } from 'chai';
|
|
18
18
|
import { Container, ContainerModule } from 'inversify';
|
|
19
19
|
import * as sinon from 'sinon';
|
|
@@ -95,6 +95,13 @@ describe('test DefaultActionDispatcher', () => {
|
|
|
95
95
|
const spy_handler1_execute = sinon.stub(handler1, 'execute').returns([]);
|
|
96
96
|
const spy_handler2_execute = sinon.stub(handler2, 'execute').returns([]);
|
|
97
97
|
const spy_handler3_execute = sinon.stub(handler3, 'execute').returns([]);
|
|
98
|
+
// Stubs created at the describe-level survive sandbox.restore(), so call counts
|
|
99
|
+
// would accumulate across tests and break `.calledOnce` assertions.
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
spy_handler1_execute.resetHistory();
|
|
102
|
+
spy_handler2_execute.resetHistory();
|
|
103
|
+
spy_handler3_execute.resetHistory();
|
|
104
|
+
});
|
|
98
105
|
const handlerMockImpl = (kind: string): ActionHandler[] => {
|
|
99
106
|
switch (kind) {
|
|
100
107
|
case action1:
|
|
@@ -129,9 +136,9 @@ describe('test DefaultActionDispatcher', () => {
|
|
|
129
136
|
// Test execution
|
|
130
137
|
await actionDispatcher.dispatchAll([{ kind: action1 }, { kind: action2 }, { kind: action3 }]);
|
|
131
138
|
// Check if all handlers have been called
|
|
132
|
-
expect(spy_handler1_execute.calledOnce);
|
|
133
|
-
expect(spy_handler2_execute.calledOnce);
|
|
134
|
-
expect(spy_handler3_execute.calledOnce);
|
|
139
|
+
expect(spy_handler1_execute.calledOnce).to.be.true;
|
|
140
|
+
expect(spy_handler2_execute.calledOnce).to.be.true;
|
|
141
|
+
expect(spy_handler3_execute.calledOnce).to.be.true;
|
|
135
142
|
// Check if all handlers have been called in the right order
|
|
136
143
|
sinon.assert.callOrder(spy_handler1_execute, spy_handler2_execute, spy_handler3_execute);
|
|
137
144
|
});
|
|
@@ -156,9 +163,9 @@ describe('test DefaultActionDispatcher', () => {
|
|
|
156
163
|
actionDispatcher.dispatch({ kind: action2 });
|
|
157
164
|
await actionDispatcher.dispatch({ kind: action3 });
|
|
158
165
|
// Check if all handlers have been called
|
|
159
|
-
expect(spy_handler1_execute.calledOnce);
|
|
160
|
-
expect(spy_handler2_execute.calledOnce);
|
|
161
|
-
expect(spy_handler3_execute.calledOnce);
|
|
166
|
+
expect(spy_handler1_execute.calledOnce).to.be.true;
|
|
167
|
+
expect(spy_handler2_execute.calledOnce).to.be.true;
|
|
168
|
+
expect(spy_handler3_execute.calledOnce).to.be.true;
|
|
162
169
|
// Check if all handlers have been called in the right order
|
|
163
170
|
sinon.assert.callOrder(spy_handler1_execute, spy_handler2_execute, spy_handler3_execute);
|
|
164
171
|
});
|
|
@@ -176,8 +183,8 @@ describe('test DefaultActionDispatcher', () => {
|
|
|
176
183
|
const spy_handler2_execute = sinon.spy(handler2, 'execute');
|
|
177
184
|
// Test execution
|
|
178
185
|
await actionDispatcher.dispatch({ kind: action1 });
|
|
179
|
-
expect(spy_handler1_execute.calledOnce);
|
|
180
|
-
expect(spy_handler2_execute.calledOnce);
|
|
186
|
+
expect(spy_handler1_execute.calledOnce).to.be.true;
|
|
187
|
+
expect(spy_handler2_execute.calledOnce).to.be.true;
|
|
181
188
|
sinon.assert.callOrder(spy_handler1_execute, spy_handler2_execute);
|
|
182
189
|
});
|
|
183
190
|
});
|
|
@@ -208,8 +215,8 @@ describe('test DefaultActionDispatcher', () => {
|
|
|
208
215
|
// Add a delay so that the action dispatcher has time to dispatch the handler response
|
|
209
216
|
await mock.delay(200);
|
|
210
217
|
// Check if all handlers have been called
|
|
211
|
-
expect(spy_requestHandler_execute.calledOnce);
|
|
212
|
-
expect(spy_responseHandler_execute.calledOnce);
|
|
218
|
+
expect(spy_requestHandler_execute.calledOnce).to.be.true;
|
|
219
|
+
expect(spy_responseHandler_execute.calledOnce).to.be.true;
|
|
213
220
|
});
|
|
214
221
|
|
|
215
222
|
it('dispatch - multiple actions & multiple response', async () => {
|
|
@@ -249,10 +256,10 @@ describe('test DefaultActionDispatcher', () => {
|
|
|
249
256
|
// Add a delay so that the action dispatcher has time to dispatch the handler response
|
|
250
257
|
await mock.delay(100);
|
|
251
258
|
// Check if all handlers have been called correctly
|
|
252
|
-
expect(spy_requestHandler1_execute.calledOnce);
|
|
253
|
-
expect(spy_requestHandler2_execute.calledOnce);
|
|
254
|
-
expect(spy_responseHandler1_execute.calledOnce);
|
|
255
|
-
expect(spy_responseHandler2_execute.calledThrice);
|
|
259
|
+
expect(spy_requestHandler1_execute.calledOnce).to.be.true;
|
|
260
|
+
expect(spy_requestHandler2_execute.calledOnce).to.be.true;
|
|
261
|
+
expect(spy_responseHandler1_execute.calledOnce).to.be.true;
|
|
262
|
+
expect(spy_responseHandler2_execute.calledThrice).to.be.true;
|
|
256
263
|
// Check if all handlers have been called in the right order
|
|
257
264
|
sinon.assert.callOrder(spy_requestHandler1_execute, spy_requestHandler2_execute);
|
|
258
265
|
sinon.assert.callOrder(spy_responseHandler1_execute, spy_responseHandler2_execute);
|
|
@@ -286,10 +293,10 @@ describe('test DefaultActionDispatcher', () => {
|
|
|
286
293
|
await actionDispatcher.dispatch({ kind: intermediateAction });
|
|
287
294
|
expect(spy_postUpdateHandler_execute.called).to.be.false;
|
|
288
295
|
await actionDispatcher.dispatch(updateModelAction);
|
|
289
|
-
expect(spy_postUpdateHandler_execute.calledOnce);
|
|
296
|
+
expect(spy_postUpdateHandler_execute.calledOnce).to.be.true;
|
|
290
297
|
// Check that action does not get dispatched again
|
|
291
298
|
await actionDispatcher.dispatch(updateModelAction);
|
|
292
|
-
expect(spy_postUpdateHandler_execute.calledOnce);
|
|
299
|
+
expect(spy_postUpdateHandler_execute.calledOnce).to.be.true;
|
|
293
300
|
});
|
|
294
301
|
});
|
|
295
302
|
|
|
@@ -502,6 +509,21 @@ describe('test DefaultActionDispatcher', () => {
|
|
|
502
509
|
expect(lateResponse.responseId).to.equal('');
|
|
503
510
|
});
|
|
504
511
|
|
|
512
|
+
it('dispatch - drops a stale ResponseAction with no matching pending request and no handler', async () => {
|
|
513
|
+
// Late `RejectAction`s (and any other ResponseAction that arrives after the matching
|
|
514
|
+
// pending request is gone) used to throw "No handler registered" because the dispatcher
|
|
515
|
+
// tried to route them as regular actions. They are protocol signals, not handler
|
|
516
|
+
// invocations — `doDispatch` swallows them with a debug log instead.
|
|
517
|
+
clientActionForwarderStub.shouldForwardToClient.returns(false);
|
|
518
|
+
clientActionForwarderStub.handle.returns(false);
|
|
519
|
+
registry_get_stub.callsFake(() => []);
|
|
520
|
+
|
|
521
|
+
const lateReject = RejectAction.create('late reject', { responseId: 'never-pending' });
|
|
522
|
+
|
|
523
|
+
// No throw, no client-forward — the dispatch resolves cleanly.
|
|
524
|
+
await actionDispatcher.dispatch(lateReject);
|
|
525
|
+
});
|
|
526
|
+
|
|
505
527
|
it('request - resolves when response intercept happens from inside doDispatch', async () => {
|
|
506
528
|
// A local handler for the request kind returns the matching response action directly.
|
|
507
529
|
// The response is dispatched via dispatchResponses() -> dispatch() and intercepted
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2026 EclipseSource and others.
|
|
3
|
-
*
|
|
4
|
-
* This program and the accompanying materials are made available under the
|
|
5
|
-
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
* http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
*
|
|
8
|
-
* This Source Code may also be made available under the following Secondary
|
|
9
|
-
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
* with the GNU Classpath Exception which is available at
|
|
12
|
-
* https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
*
|
|
14
|
-
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
|
-
********************************************************************************/
|
|
16
|
-
import { Action } from '@eclipse-glsp/protocol';
|
|
17
|
-
import { ActionDispatchContext } from '../../common/actions/action-dispatcher';
|
|
18
|
-
/**
|
|
19
|
-
* Browser-compatible {@link ActionDispatchContext} that uses a simple flag instead of
|
|
20
|
-
* `AsyncLocalStorage`. V8's `await` uses the spec's `PerformPromiseThen` which bypasses
|
|
21
|
-
* patched `Promise.prototype.then`, breaking polyfill-based `AsyncLocalStorage` context
|
|
22
|
-
* propagation across `await` boundaries. This causes deadlocks in the action dispatcher's
|
|
23
|
-
* consumer loop.
|
|
24
|
-
*
|
|
25
|
-
* Since web workers are single-threaded with a single consumer loop, a flag-based approach
|
|
26
|
-
* is sufficient. The flag is set before processing and cleared via `promise.finally()` so
|
|
27
|
-
* it persists across the entire async chain.
|
|
28
|
-
*
|
|
29
|
-
* Unlike native `AsyncLocalStorage`, the flag cannot distinguish "within the handler's async
|
|
30
|
-
* continuation" from "new event that arrived during an await gap." To preserve sequential
|
|
31
|
-
* processing guarantees, {@link isInContext} always returns `false` for client-originated
|
|
32
|
-
* actions, ensuring they are queued rather than dispatched inline.
|
|
33
|
-
*/
|
|
34
|
-
export declare class BrowserDispatchContext implements ActionDispatchContext {
|
|
35
|
-
protected active: boolean;
|
|
36
|
-
run<R>(callback: () => R): R;
|
|
37
|
-
isInContext(action: Action): boolean;
|
|
38
|
-
}
|
|
39
|
-
//# sourceMappingURL=browser-dispatch-context.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"browser-dispatch-context.d.ts","sourceRoot":"","sources":["../../../src/browser/di/browser-dispatch-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;kFAckF;AAElF,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAG/E;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,sBAAuB,YAAW,qBAAqB;IAChE,SAAS,CAAC,MAAM,UAAS;IAEzB,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC;IAa5B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CAGvC"}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/********************************************************************************
|
|
3
|
-
* Copyright (c) 2026 EclipseSource and others.
|
|
4
|
-
*
|
|
5
|
-
* This program and the accompanying materials are made available under the
|
|
6
|
-
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
-
* http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
-
*
|
|
9
|
-
* This Source Code may also be made available under the following Secondary
|
|
10
|
-
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
-
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
-
* with the GNU Classpath Exception which is available at
|
|
13
|
-
* https://www.gnu.org/software/classpath/license.html.
|
|
14
|
-
*
|
|
15
|
-
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
16
|
-
********************************************************************************/
|
|
17
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.BrowserDispatchContext = void 0;
|
|
19
|
-
const client_action_1 = require("../../common/protocol/client-action");
|
|
20
|
-
/**
|
|
21
|
-
* Browser-compatible {@link ActionDispatchContext} that uses a simple flag instead of
|
|
22
|
-
* `AsyncLocalStorage`. V8's `await` uses the spec's `PerformPromiseThen` which bypasses
|
|
23
|
-
* patched `Promise.prototype.then`, breaking polyfill-based `AsyncLocalStorage` context
|
|
24
|
-
* propagation across `await` boundaries. This causes deadlocks in the action dispatcher's
|
|
25
|
-
* consumer loop.
|
|
26
|
-
*
|
|
27
|
-
* Since web workers are single-threaded with a single consumer loop, a flag-based approach
|
|
28
|
-
* is sufficient. The flag is set before processing and cleared via `promise.finally()` so
|
|
29
|
-
* it persists across the entire async chain.
|
|
30
|
-
*
|
|
31
|
-
* Unlike native `AsyncLocalStorage`, the flag cannot distinguish "within the handler's async
|
|
32
|
-
* continuation" from "new event that arrived during an await gap." To preserve sequential
|
|
33
|
-
* processing guarantees, {@link isInContext} always returns `false` for client-originated
|
|
34
|
-
* actions, ensuring they are queued rather than dispatched inline.
|
|
35
|
-
*/
|
|
36
|
-
class BrowserDispatchContext {
|
|
37
|
-
constructor() {
|
|
38
|
-
this.active = false;
|
|
39
|
-
}
|
|
40
|
-
run(callback) {
|
|
41
|
-
const prior = this.active;
|
|
42
|
-
this.active = true;
|
|
43
|
-
const result = callback();
|
|
44
|
-
if (result instanceof Promise) {
|
|
45
|
-
return result.finally(() => {
|
|
46
|
-
this.active = prior;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
this.active = prior;
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
isInContext(action) {
|
|
53
|
-
return this.active && !client_action_1.ClientAction.is(action);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
exports.BrowserDispatchContext = BrowserDispatchContext;
|
|
57
|
-
//# sourceMappingURL=browser-dispatch-context.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"browser-dispatch-context.js","sourceRoot":"","sources":["../../../src/browser/di/browser-dispatch-context.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;kFAckF;;;AAIlF,uEAAmE;AAEnE;;;;;;;;;;;;;;;GAeG;AACH,MAAa,sBAAsB;IAAnC;QACc,WAAM,GAAG,KAAK,CAAC;IAkB7B,CAAC;IAhBG,GAAG,CAAI,QAAiB;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC1B,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;gBACvB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACxB,CAAC,CAAiB,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,WAAW,CAAC,MAAc;QACtB,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,4BAAY,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;CACJ;AAnBD,wDAmBC"}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2026 EclipseSource and others.
|
|
3
|
-
*
|
|
4
|
-
* This program and the accompanying materials are made available under the
|
|
5
|
-
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
* http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
*
|
|
8
|
-
* This Source Code may also be made available under the following Secondary
|
|
9
|
-
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
* with the GNU Classpath Exception which is available at
|
|
12
|
-
* https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
*
|
|
14
|
-
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
|
-
********************************************************************************/
|
|
16
|
-
import { Action } from '@eclipse-glsp/protocol';
|
|
17
|
-
import { ActionDispatchContext } from '../../common/actions/action-dispatcher';
|
|
18
|
-
/**
|
|
19
|
-
* Browser-compatible {@link ActionDispatchContext} that uses a simple flag instead of
|
|
20
|
-
* `AsyncLocalStorage`. V8's `await` uses the spec's `PerformPromiseThen` which bypasses
|
|
21
|
-
* patched `Promise.prototype.then`, breaking polyfill-based `AsyncLocalStorage` context
|
|
22
|
-
* propagation across `await` boundaries. This causes deadlocks in the action dispatcher's
|
|
23
|
-
* consumer loop.
|
|
24
|
-
*
|
|
25
|
-
* Since web workers are single-threaded with a single consumer loop, a flag-based approach
|
|
26
|
-
* is sufficient. The flag is set before processing and cleared via `promise.finally()` so
|
|
27
|
-
* it persists across the entire async chain.
|
|
28
|
-
*
|
|
29
|
-
* Unlike native `AsyncLocalStorage`, the flag cannot distinguish "within the handler's async
|
|
30
|
-
* continuation" from "new event that arrived during an await gap." To preserve sequential
|
|
31
|
-
* processing guarantees, {@link isInContext} always returns `false` for client-originated
|
|
32
|
-
* actions, ensuring they are queued rather than dispatched inline.
|
|
33
|
-
*/
|
|
34
|
-
export declare class SyncDispatchContext implements ActionDispatchContext {
|
|
35
|
-
protected active: boolean;
|
|
36
|
-
run<R>(callback: () => R): R;
|
|
37
|
-
isInContext(action: Action): boolean;
|
|
38
|
-
}
|
|
39
|
-
//# sourceMappingURL=sync-dispatch-context.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sync-dispatch-context.d.ts","sourceRoot":"","sources":["../../../src/browser/di/sync-dispatch-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;kFAckF;AAElF,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAG/E;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,mBAAoB,YAAW,qBAAqB;IAC7D,SAAS,CAAC,MAAM,UAAS;IAEzB,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC;IAa5B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CAGvC"}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/********************************************************************************
|
|
3
|
-
* Copyright (c) 2026 EclipseSource and others.
|
|
4
|
-
*
|
|
5
|
-
* This program and the accompanying materials are made available under the
|
|
6
|
-
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
-
* http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
-
*
|
|
9
|
-
* This Source Code may also be made available under the following Secondary
|
|
10
|
-
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
-
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
-
* with the GNU Classpath Exception which is available at
|
|
13
|
-
* https://www.gnu.org/software/classpath/license.html.
|
|
14
|
-
*
|
|
15
|
-
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
16
|
-
********************************************************************************/
|
|
17
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.SyncDispatchContext = void 0;
|
|
19
|
-
const client_action_1 = require("../../common/protocol/client-action");
|
|
20
|
-
/**
|
|
21
|
-
* Browser-compatible {@link ActionDispatchContext} that uses a simple flag instead of
|
|
22
|
-
* `AsyncLocalStorage`. V8's `await` uses the spec's `PerformPromiseThen` which bypasses
|
|
23
|
-
* patched `Promise.prototype.then`, breaking polyfill-based `AsyncLocalStorage` context
|
|
24
|
-
* propagation across `await` boundaries. This causes deadlocks in the action dispatcher's
|
|
25
|
-
* consumer loop.
|
|
26
|
-
*
|
|
27
|
-
* Since web workers are single-threaded with a single consumer loop, a flag-based approach
|
|
28
|
-
* is sufficient. The flag is set before processing and cleared via `promise.finally()` so
|
|
29
|
-
* it persists across the entire async chain.
|
|
30
|
-
*
|
|
31
|
-
* Unlike native `AsyncLocalStorage`, the flag cannot distinguish "within the handler's async
|
|
32
|
-
* continuation" from "new event that arrived during an await gap." To preserve sequential
|
|
33
|
-
* processing guarantees, {@link isInContext} always returns `false` for client-originated
|
|
34
|
-
* actions, ensuring they are queued rather than dispatched inline.
|
|
35
|
-
*/
|
|
36
|
-
class SyncDispatchContext {
|
|
37
|
-
constructor() {
|
|
38
|
-
this.active = false;
|
|
39
|
-
}
|
|
40
|
-
run(callback) {
|
|
41
|
-
const prior = this.active;
|
|
42
|
-
this.active = true;
|
|
43
|
-
const result = callback();
|
|
44
|
-
if (result instanceof Promise) {
|
|
45
|
-
return result.finally(() => {
|
|
46
|
-
this.active = prior;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
this.active = prior;
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
isInContext(action) {
|
|
53
|
-
return this.active && !client_action_1.ClientAction.is(action);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
exports.SyncDispatchContext = SyncDispatchContext;
|
|
57
|
-
//# sourceMappingURL=sync-dispatch-context.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sync-dispatch-context.js","sourceRoot":"","sources":["../../../src/browser/di/sync-dispatch-context.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;kFAckF;;;AAIlF,uEAAmE;AAEnE;;;;;;;;;;;;;;;GAeG;AACH,MAAa,mBAAmB;IAAhC;QACc,WAAM,GAAG,KAAK,CAAC;IAkB7B,CAAC;IAhBG,GAAG,CAAI,QAAiB;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC1B,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;gBACvB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACxB,CAAC,CAAiB,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,WAAW,CAAC,MAAc;QACtB,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,4BAAY,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;CACJ;AAnBD,kDAmBC"}
|