@opensumi/ide-connection 2.27.3-rc-1713838390.0 → 2.27.3-rc-1714116491.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/capturer.d.ts +21 -1
- package/lib/common/capturer.d.ts.map +1 -1
- package/lib/common/capturer.js +130 -4
- package/lib/common/capturer.js.map +1 -1
- package/lib/common/index.d.ts +0 -1
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +0 -1
- package/lib/common/index.js.map +1 -1
- package/lib/common/rpc/connection.d.ts +8 -2
- package/lib/common/rpc/connection.d.ts.map +1 -1
- package/lib/common/rpc/connection.js +17 -6
- package/lib/common/rpc/connection.js.map +1 -1
- package/lib/common/rpc/multiplexer.d.ts +9 -6
- package/lib/common/rpc/multiplexer.d.ts.map +1 -1
- package/lib/common/rpc/multiplexer.js +23 -23
- package/lib/common/rpc/multiplexer.js.map +1 -1
- package/lib/common/rpc-service/center.d.ts +1 -1
- package/lib/common/rpc-service/center.d.ts.map +1 -1
- package/lib/common/rpc-service/center.js.map +1 -1
- package/lib/common/rpc-service/proxy/base.d.ts +2 -11
- package/lib/common/rpc-service/proxy/base.d.ts.map +1 -1
- package/lib/common/rpc-service/proxy/base.js +3 -82
- package/lib/common/rpc-service/proxy/base.js.map +1 -1
- package/lib/common/rpc-service/proxy/json.d.ts +3 -1
- package/lib/common/rpc-service/proxy/json.d.ts.map +1 -1
- package/lib/common/rpc-service/proxy/json.js +12 -10
- package/lib/common/rpc-service/proxy/json.js.map +1 -1
- package/lib/common/rpc-service/proxy/sumi.d.ts.map +1 -1
- package/lib/common/rpc-service/proxy/sumi.js +3 -31
- package/lib/common/rpc-service/proxy/sumi.js.map +1 -1
- package/lib/common/rpc-service/registry.d.ts +1 -1
- package/lib/common/rpc-service/registry.d.ts.map +1 -1
- package/lib/common/rpc-service/registry.js +16 -15
- package/lib/common/rpc-service/registry.js.map +1 -1
- package/lib/common/ws-channel.d.ts +0 -1
- package/lib/common/ws-channel.d.ts.map +1 -1
- package/lib/common/ws-channel.js +0 -4
- package/lib/common/ws-channel.js.map +1 -1
- package/package.json +5 -5
- package/src/common/capturer.ts +169 -3
- package/src/common/index.ts +0 -1
- package/src/common/rpc/connection.ts +34 -7
- package/src/common/rpc/multiplexer.ts +31 -26
- package/src/common/rpc-service/center.ts +2 -1
- package/src/common/rpc-service/proxy/base.ts +5 -101
- package/src/common/rpc-service/proxy/json.ts +14 -12
- package/src/common/rpc-service/proxy/sumi.ts +3 -30
- package/src/common/rpc-service/registry.ts +20 -15
- package/src/common/ws-channel.ts +0 -4
package/src/common/capturer.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DisposableStore,
|
|
3
|
+
IDisposable,
|
|
4
|
+
isUint8Array,
|
|
5
|
+
randomString,
|
|
6
|
+
transformErrorForSerialization,
|
|
7
|
+
} from '@opensumi/ide-core-common';
|
|
8
|
+
import { DevtoolsLantencyCommand, EDevtoolsEvent } from '@opensumi/ide-core-common/lib/devtools';
|
|
9
|
+
|
|
1
10
|
declare global {
|
|
2
11
|
interface Window {
|
|
3
12
|
__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__: any;
|
|
@@ -22,15 +31,172 @@ export interface ICapturedMessage {
|
|
|
22
31
|
type: MessageType;
|
|
23
32
|
serviceMethod: string;
|
|
24
33
|
arguments?: any;
|
|
25
|
-
requestId?: string;
|
|
34
|
+
requestId?: string | number;
|
|
26
35
|
status?: ResponseStatus;
|
|
27
36
|
data?: any;
|
|
28
37
|
error?: any;
|
|
38
|
+
|
|
39
|
+
source?: string;
|
|
29
40
|
}
|
|
30
41
|
|
|
42
|
+
const _global = (typeof window !== 'undefined' ? window : global) || {
|
|
43
|
+
__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__: undefined,
|
|
44
|
+
};
|
|
45
|
+
|
|
31
46
|
export function getCapturer() {
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
const hook = _global.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__;
|
|
48
|
+
if (hook) {
|
|
49
|
+
return hook.captureRPC;
|
|
34
50
|
}
|
|
35
51
|
return;
|
|
36
52
|
}
|
|
53
|
+
|
|
54
|
+
export class Capturer implements IDisposable {
|
|
55
|
+
protected _disposables = new DisposableStore();
|
|
56
|
+
|
|
57
|
+
protected capturer: ((data: any) => void) | null = null;
|
|
58
|
+
protected prefix: string;
|
|
59
|
+
|
|
60
|
+
protected setupListener = (event: CustomEvent) => {
|
|
61
|
+
const { command } = event.detail;
|
|
62
|
+
if (command === DevtoolsLantencyCommand.Start) {
|
|
63
|
+
this.capturer = getCapturer();
|
|
64
|
+
} else if (command === DevtoolsLantencyCommand.Stop) {
|
|
65
|
+
this.capturer = null;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
constructor(protected source: string) {
|
|
70
|
+
this.prefix = randomString(6);
|
|
71
|
+
this.capturer = getCapturer();
|
|
72
|
+
|
|
73
|
+
// capturer should only be used in browser environment
|
|
74
|
+
if (typeof _global.addEventListener === 'function') {
|
|
75
|
+
_global.addEventListener(EDevtoolsEvent.Latency, this.setupListener);
|
|
76
|
+
this._disposables.add({
|
|
77
|
+
dispose: () => {
|
|
78
|
+
_global.removeEventListener(EDevtoolsEvent.Latency, this.setupListener);
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
capture(message: ICapturedMessage): void {
|
|
85
|
+
if (!this.capturer) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const data: ICapturedMessage = {
|
|
90
|
+
...message,
|
|
91
|
+
source: this.source,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
if (data.data) {
|
|
95
|
+
if (isUint8Array(data.data)) {
|
|
96
|
+
data.data = '<Uint8Array>';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (message.requestId) {
|
|
101
|
+
data.requestId = `${this.prefix}-${message.requestId}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (message.error) {
|
|
105
|
+
data.error = transformErrorForSerialization(message.error);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.capturer(data);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
captureOnRequest(requestId: ICapturedMessage['requestId'], serviceMethod: string, args: any[]): void {
|
|
112
|
+
if (!this.capturer) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.capture({ type: MessageType.OnRequest, requestId: `↓${requestId}`, serviceMethod, arguments: args });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
captureOnRequestResult(requestId: ICapturedMessage['requestId'], serviceMethod: string, data: any): void {
|
|
120
|
+
if (!this.capturer) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.capture({
|
|
125
|
+
type: MessageType.OnRequestResult,
|
|
126
|
+
status: ResponseStatus.Success,
|
|
127
|
+
requestId: `↓${requestId}`,
|
|
128
|
+
serviceMethod,
|
|
129
|
+
data,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
captureOnRequestFail(requestId: ICapturedMessage['requestId'], serviceMethod: string, error: any): void {
|
|
134
|
+
if (!this.capturer) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.capture({
|
|
139
|
+
type: MessageType.OnRequestResult,
|
|
140
|
+
status: ResponseStatus.Fail,
|
|
141
|
+
requestId: `↓${requestId}`,
|
|
142
|
+
serviceMethod,
|
|
143
|
+
error,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
captureSendRequest(requestId: ICapturedMessage['requestId'] | number, serviceMethod: string, args: any[]): void {
|
|
148
|
+
if (!this.capturer) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.capture({ type: MessageType.SendRequest, requestId, serviceMethod, arguments: args });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
captureSendRequestResult(requestId: ICapturedMessage['requestId'], serviceMethod: string, data: any): void {
|
|
156
|
+
if (!this.capturer) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this.capture({
|
|
161
|
+
type: MessageType.RequestResult,
|
|
162
|
+
status: ResponseStatus.Success,
|
|
163
|
+
requestId,
|
|
164
|
+
serviceMethod,
|
|
165
|
+
data,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
captureSendRequestFail(requestId: ICapturedMessage['requestId'], serviceMethod: string, error: any): void {
|
|
170
|
+
if (!this.capturer) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
this.capture({
|
|
175
|
+
type: MessageType.RequestResult,
|
|
176
|
+
status: ResponseStatus.Fail,
|
|
177
|
+
requestId,
|
|
178
|
+
serviceMethod,
|
|
179
|
+
error,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
captureSendNotification(requestId: ICapturedMessage['requestId'], serviceMethod: string, args: any[]): void {
|
|
184
|
+
if (!this.capturer) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.capture({ type: MessageType.SendNotification, serviceMethod, arguments: args, requestId });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
captureOnNotification(requestId: ICapturedMessage['requestId'], serviceMethod: string, args: any[]): void {
|
|
192
|
+
if (!this.capturer) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this.capture({ type: MessageType.OnNotification, serviceMethod, arguments: args, requestId: `↓${requestId}` });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
dispose(): void {
|
|
200
|
+
this._disposables.dispose();
|
|
201
|
+
}
|
|
202
|
+
}
|
package/src/common/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { getDebugLogger } from '@opensumi/ide-core-common';
|
|
|
2
2
|
import {
|
|
3
3
|
CancellationToken,
|
|
4
4
|
CancellationTokenSource,
|
|
5
|
-
|
|
5
|
+
DisposableStore,
|
|
6
6
|
EventQueue,
|
|
7
7
|
IDisposable,
|
|
8
8
|
canceled,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '@opensumi/ide-utils';
|
|
11
11
|
import { IReadableStream, isReadableStream, listenReadable } from '@opensumi/ide-utils/lib/stream';
|
|
12
12
|
|
|
13
|
+
import { Capturer } from '../capturer';
|
|
13
14
|
import { BaseConnection, NetSocketConnection, WSWebSocketConnection } from '../connection';
|
|
14
15
|
import { METHOD_NOT_REGISTERED } from '../constants';
|
|
15
16
|
import { ILogger } from '../types';
|
|
@@ -34,10 +35,14 @@ const nullHeaders = {};
|
|
|
34
35
|
export interface ISumiConnectionOptions {
|
|
35
36
|
timeout?: number;
|
|
36
37
|
logger?: ILogger;
|
|
38
|
+
/**
|
|
39
|
+
* The name of the connection, used for debugging(and can see in opensumi-devtools).
|
|
40
|
+
*/
|
|
41
|
+
name?: string;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
export class SumiConnection implements IDisposable {
|
|
40
|
-
protected disposable = new
|
|
45
|
+
protected disposable = new DisposableStore();
|
|
41
46
|
|
|
42
47
|
private _requestHandlers = new Map<string, TGenericRequestHandler<any>>();
|
|
43
48
|
private _starRequestHandler: TRequestNotFoundHandler | undefined;
|
|
@@ -56,16 +61,24 @@ export class SumiConnection implements IDisposable {
|
|
|
56
61
|
public io = new MessageIO();
|
|
57
62
|
protected logger: ILogger;
|
|
58
63
|
|
|
64
|
+
protected capturer: Capturer;
|
|
65
|
+
|
|
59
66
|
constructor(protected socket: BaseConnection<Uint8Array>, protected options: ISumiConnectionOptions = {}) {
|
|
60
67
|
if (options.logger) {
|
|
61
68
|
this.logger = options.logger;
|
|
62
69
|
} else {
|
|
63
70
|
this.logger = getDebugLogger();
|
|
64
71
|
}
|
|
72
|
+
|
|
73
|
+
this.capturer = new Capturer(options.name || 'sumi');
|
|
74
|
+
this.disposable.add(this.capturer);
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
sendNotification(method: string, ...args: any[]) {
|
|
68
|
-
this.
|
|
78
|
+
const requestId = this._requestId++;
|
|
79
|
+
|
|
80
|
+
this.capturer.captureSendNotification(requestId, method, args);
|
|
81
|
+
this.socket.send(this.io.Notification(requestId, method, nullHeaders, args));
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
sendRequest(method: string, ...args: any[]) {
|
|
@@ -74,6 +87,8 @@ export class SumiConnection implements IDisposable {
|
|
|
74
87
|
|
|
75
88
|
this._callbacks.set(requestId, (headers, error, result) => {
|
|
76
89
|
if (error) {
|
|
90
|
+
this.traceRequestError(requestId, method, args, error);
|
|
91
|
+
|
|
77
92
|
if (error === METHOD_NOT_REGISTERED) {
|
|
78
93
|
// we should not treat `METHOD_NOT_REGISTERED` as an error.
|
|
79
94
|
// it is a special case, it means the method is not registered on the other side.
|
|
@@ -81,11 +96,12 @@ export class SumiConnection implements IDisposable {
|
|
|
81
96
|
return;
|
|
82
97
|
}
|
|
83
98
|
|
|
84
|
-
this.traceRequestError(method, args, error);
|
|
85
99
|
reject(error);
|
|
86
100
|
return;
|
|
87
101
|
}
|
|
88
102
|
|
|
103
|
+
this.capturer.captureSendRequestResult(requestId, method, result);
|
|
104
|
+
|
|
89
105
|
resolve(result);
|
|
90
106
|
});
|
|
91
107
|
|
|
@@ -107,6 +123,8 @@ export class SumiConnection implements IDisposable {
|
|
|
107
123
|
cancellationToken.onCancellationRequested(() => this.cancelRequest(requestId));
|
|
108
124
|
}
|
|
109
125
|
|
|
126
|
+
this.capturer.captureSendRequest(requestId, method, args);
|
|
127
|
+
|
|
110
128
|
this.socket.send(
|
|
111
129
|
this.io.Request(
|
|
112
130
|
requestId,
|
|
@@ -267,6 +285,8 @@ export class SumiConnection implements IDisposable {
|
|
|
267
285
|
|
|
268
286
|
switch (opType) {
|
|
269
287
|
case OperationType.Request: {
|
|
288
|
+
this.capturer.captureOnRequest(requestId, method, args);
|
|
289
|
+
|
|
270
290
|
let promise: Promise<any>;
|
|
271
291
|
|
|
272
292
|
try {
|
|
@@ -285,6 +305,8 @@ export class SumiConnection implements IDisposable {
|
|
|
285
305
|
}
|
|
286
306
|
|
|
287
307
|
const onSuccess = (result: any) => {
|
|
308
|
+
this.capturer.captureOnRequestResult(requestId, method, result);
|
|
309
|
+
|
|
288
310
|
if (isReadableStream(result)) {
|
|
289
311
|
const responseHeaders: IResponseHeaders = {
|
|
290
312
|
chunked: true,
|
|
@@ -305,7 +327,8 @@ export class SumiConnection implements IDisposable {
|
|
|
305
327
|
};
|
|
306
328
|
|
|
307
329
|
const onError = (err: Error) => {
|
|
308
|
-
this.traceRequestError(method, args, err);
|
|
330
|
+
this.traceRequestError(requestId, method, args, err);
|
|
331
|
+
|
|
309
332
|
this.socket.send(this.io.Error(requestId, method, nullHeaders, err));
|
|
310
333
|
this._cancellationTokenSources.delete(requestId);
|
|
311
334
|
};
|
|
@@ -314,7 +337,10 @@ export class SumiConnection implements IDisposable {
|
|
|
314
337
|
break;
|
|
315
338
|
}
|
|
316
339
|
case OperationType.Notification: {
|
|
340
|
+
this.capturer.captureOnNotification(requestId, method, args);
|
|
341
|
+
|
|
317
342
|
const handler = this._notificationHandlers.get(method);
|
|
343
|
+
|
|
318
344
|
if (handler) {
|
|
319
345
|
handler(...args);
|
|
320
346
|
} else if (this._starNotificationHandler) {
|
|
@@ -341,7 +367,7 @@ export class SumiConnection implements IDisposable {
|
|
|
341
367
|
}
|
|
342
368
|
});
|
|
343
369
|
if (toDispose) {
|
|
344
|
-
this.disposable.
|
|
370
|
+
this.disposable.add(toDispose);
|
|
345
371
|
}
|
|
346
372
|
}
|
|
347
373
|
|
|
@@ -357,7 +383,8 @@ export class SumiConnection implements IDisposable {
|
|
|
357
383
|
return new SumiConnection(new NetSocketConnection(socket), options);
|
|
358
384
|
}
|
|
359
385
|
|
|
360
|
-
private traceRequestError(method: string, args: any[], error: any) {
|
|
386
|
+
private traceRequestError(requestId: number, method: string, args: any[], error: any) {
|
|
387
|
+
this.capturer.captureSendRequestFail(requestId, method, error);
|
|
361
388
|
this.logger.error(`Error handling request ${method} with args `, args, error);
|
|
362
389
|
}
|
|
363
390
|
}
|
|
@@ -24,34 +24,35 @@ export interface IRPCProtocol {
|
|
|
24
24
|
get<T>(identifier: ProxyIdentifier<T>): T;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const SEP = '||';
|
|
28
|
-
const SEP_LENGTH = SEP.length;
|
|
29
|
-
|
|
30
|
-
export function getRPCName(serviceId: string, methodName: string) {
|
|
31
|
-
return `${serviceId}${SEP}${methodName}`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function extractServiceAndMethod(rpcId: string): [string, string] {
|
|
35
|
-
const idx = rpcId.indexOf(SEP);
|
|
36
|
-
return [rpcId.substring(0, idx), rpcId.substring(idx + SEP_LENGTH)];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
27
|
/**
|
|
40
28
|
* A connection multiplexer that allows to register multiple local RPC services and to create proxies for them.
|
|
41
29
|
*/
|
|
42
30
|
export class SumiConnectionMultiplexer extends SumiConnection implements IRPCProtocol {
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
protected static SEP = '/';
|
|
32
|
+
protected static SEP_LENGTH = SumiConnectionMultiplexer.SEP.length;
|
|
33
|
+
|
|
34
|
+
protected static getRPCName(serviceId: string, methodName: string) {
|
|
35
|
+
return `${serviceId}${SumiConnectionMultiplexer.SEP}${methodName}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected static extractServiceAndMethod(rpcId: string): [string, string] {
|
|
39
|
+
const idx = rpcId.indexOf(SumiConnectionMultiplexer.SEP);
|
|
40
|
+
return [rpcId.substring(0, idx), rpcId.substring(idx + SumiConnectionMultiplexer.SEP_LENGTH)];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected static normalizeServiceId(serviceId: string) {
|
|
44
|
+
return serviceId.replace(/\//g, '_');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected readonly _locals: Map<string, any>;
|
|
48
|
+
protected readonly _proxies: Map<string, any>;
|
|
45
49
|
|
|
46
50
|
constructor(protected socket: BaseConnection<Uint8Array>, protected options: ISumiConnectionOptions = {}) {
|
|
47
51
|
super(socket, options);
|
|
48
52
|
this._locals = new Map();
|
|
49
53
|
this._proxies = new Map();
|
|
50
54
|
|
|
51
|
-
this.onRequestNotFound((rpcName: string, args: any[]) =>
|
|
52
|
-
const [rpcId, methodName] = extractServiceAndMethod(rpcName);
|
|
53
|
-
return this._doInvokeHandler(rpcId, methodName, args);
|
|
54
|
-
});
|
|
55
|
+
this.onRequestNotFound((rpcName: string, args: any[]) => this._doInvokeHandler(rpcName, args));
|
|
55
56
|
|
|
56
57
|
// call `listen` implicitly
|
|
57
58
|
// compatible behavior with the RPCProtocol
|
|
@@ -59,23 +60,25 @@ export class SumiConnectionMultiplexer extends SumiConnection implements IRPCPro
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
public set<T>(identifier: ProxyIdentifier<T>, instance: any) {
|
|
62
|
-
this._locals.set(identifier.serviceId, instance);
|
|
63
|
+
this._locals.set(SumiConnectionMultiplexer.normalizeServiceId(identifier.serviceId), instance);
|
|
63
64
|
return instance;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
public get<T>(identifier: ProxyIdentifier<T>) {
|
|
67
|
-
return this._locals.get(identifier.serviceId);
|
|
68
|
+
return this._locals.get(SumiConnectionMultiplexer.normalizeServiceId(identifier.serviceId));
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
public getProxy<T>(proxyId: ProxyIdentifier<T>) {
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
const serviceId = SumiConnectionMultiplexer.normalizeServiceId(proxyId.serviceId);
|
|
73
|
+
|
|
74
|
+
if (!this._proxies.has(serviceId)) {
|
|
75
|
+
this._proxies.set(serviceId, this._createProxy(serviceId));
|
|
73
76
|
}
|
|
74
77
|
|
|
75
|
-
return this._proxies.get(
|
|
78
|
+
return this._proxies.get(serviceId);
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
|
|
81
|
+
protected _createProxy(rpcId: string) {
|
|
79
82
|
const handler = {
|
|
80
83
|
get: (target: any, name: string) => {
|
|
81
84
|
if (typeof name === 'symbol') {
|
|
@@ -83,7 +86,7 @@ export class SumiConnectionMultiplexer extends SumiConnection implements IRPCPro
|
|
|
83
86
|
}
|
|
84
87
|
// charCodeAt(0) === 36 means starts with $
|
|
85
88
|
if (!target[name] && name.charCodeAt(0) === 36) {
|
|
86
|
-
const rpcName = getRPCName(rpcId, name);
|
|
89
|
+
const rpcName = SumiConnectionMultiplexer.getRPCName(rpcId, name);
|
|
87
90
|
target[name] = (...args: any[]) => this.sendRequest(rpcName, ...args);
|
|
88
91
|
}
|
|
89
92
|
|
|
@@ -94,7 +97,9 @@ export class SumiConnectionMultiplexer extends SumiConnection implements IRPCPro
|
|
|
94
97
|
return new Proxy(Object.create(null), handler);
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
protected async _doInvokeHandler(rpcName: string, args: any[]): Promise<any> {
|
|
101
|
+
const [rpcId, methodName] = SumiConnectionMultiplexer.extractServiceAndMethod(rpcName);
|
|
102
|
+
|
|
98
103
|
const actor = this._locals.get(rpcId);
|
|
99
104
|
if (!actor) {
|
|
100
105
|
throw new Error('Unknown actor ' + rpcId);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Deferred } from '@opensumi/ide-core-common';
|
|
2
|
-
import { MessageConnection } from '@opensumi/vscode-jsonrpc';
|
|
3
2
|
|
|
4
3
|
import { METHOD_NOT_REGISTERED } from '../constants';
|
|
5
4
|
import { TSumiProtocol } from '../rpc';
|
|
@@ -10,6 +9,8 @@ import { ProxyJson, ProxySumi } from './proxy';
|
|
|
10
9
|
import { ProxyBase } from './proxy/base';
|
|
11
10
|
import { ProtocolRegistry, ServiceRegistry } from './registry';
|
|
12
11
|
|
|
12
|
+
import type { MessageConnection } from '@opensumi/vscode-jsonrpc';
|
|
13
|
+
|
|
13
14
|
const safeProcess: { pid: string } = typeof process === 'undefined' ? { pid: 'unknown' } : (process as any);
|
|
14
15
|
|
|
15
16
|
export class RPCServiceCenter {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { Deferred } from '@opensumi/ide-core-common';
|
|
1
|
+
import { Deferred, DisposableStore } from '@opensumi/ide-core-common';
|
|
2
2
|
|
|
3
|
-
import { ICapturedMessage, MessageType, ResponseStatus, getCapturer } from '../../capturer';
|
|
4
3
|
import { ILogger, IRPCServiceMap } from '../../types';
|
|
5
4
|
|
|
6
5
|
import type { ServiceRegistry } from '../registry';
|
|
@@ -20,14 +19,13 @@ export abstract class ProxyBase<T extends IBaseConnection> {
|
|
|
20
19
|
|
|
21
20
|
protected connectionPromise: Deferred<void> = new Deferred<void>();
|
|
22
21
|
|
|
22
|
+
protected _disposables = new DisposableStore();
|
|
23
|
+
|
|
23
24
|
protected abstract engine: 'json' | 'sumi';
|
|
24
|
-
capturer: (data: any) => void;
|
|
25
25
|
|
|
26
26
|
constructor(public registry: ServiceRegistry, logger?: ILogger) {
|
|
27
27
|
this.logger = logger || console;
|
|
28
28
|
|
|
29
|
-
this.capturer = getCapturer();
|
|
30
|
-
|
|
31
29
|
this.registry.onServicesUpdate((services) => {
|
|
32
30
|
if (this.connection) {
|
|
33
31
|
this.bindMethods(services);
|
|
@@ -35,105 +33,13 @@ export abstract class ProxyBase<T extends IBaseConnection> {
|
|
|
35
33
|
});
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
// capture messages for opensumi devtools
|
|
39
|
-
private capture(message: ICapturedMessage): void {
|
|
40
|
-
this.capturer({
|
|
41
|
-
...message,
|
|
42
|
-
engine: this.engine,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
36
|
protected nextRequestId() {
|
|
47
37
|
return String(requestId++);
|
|
48
38
|
}
|
|
49
39
|
|
|
50
|
-
protected captureOnRequest(requestId: string, serviceMethod: string, args: any[]): void {
|
|
51
|
-
if (!this.capturer) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
this.capture({ type: MessageType.OnRequest, requestId, serviceMethod, arguments: args });
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
protected captureOnRequestResult(requestId: string, serviceMethod: string, data: any): void {
|
|
58
|
-
if (!this.capturer) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
this.capture({
|
|
62
|
-
type: MessageType.OnRequestResult,
|
|
63
|
-
status: ResponseStatus.Success,
|
|
64
|
-
requestId,
|
|
65
|
-
serviceMethod,
|
|
66
|
-
data,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
protected captureOnRequestFail(requestId: string, serviceMethod: string, error: any): void {
|
|
71
|
-
if (!this.capturer) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
this.logger.warn(`request exec ${serviceMethod} error`, error);
|
|
76
|
-
|
|
77
|
-
this.capture({
|
|
78
|
-
type: MessageType.OnRequestResult,
|
|
79
|
-
status: ResponseStatus.Fail,
|
|
80
|
-
requestId,
|
|
81
|
-
serviceMethod,
|
|
82
|
-
error,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
protected captureOnNotification(serviceMethod: string, args: any[]): void {
|
|
87
|
-
if (!this.capturer) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
this.capture({ type: MessageType.OnNotification, serviceMethod, arguments: args });
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
protected captureSendRequest(requestId: string, serviceMethod: string, args: any[]): void {
|
|
94
|
-
if (!this.capturer) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
this.capture({ type: MessageType.SendRequest, requestId, serviceMethod, arguments: args });
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
protected captureSendRequestResult(requestId: string, serviceMethod: string, data: any): void {
|
|
101
|
-
if (!this.capturer) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
this.capture({
|
|
106
|
-
type: MessageType.RequestResult,
|
|
107
|
-
status: ResponseStatus.Success,
|
|
108
|
-
requestId,
|
|
109
|
-
serviceMethod,
|
|
110
|
-
data,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
protected captureSendRequestFail(requestId: string, serviceMethod: string, error: any): void {
|
|
115
|
-
if (!this.capturer) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
this.capture({
|
|
120
|
-
type: MessageType.RequestResult,
|
|
121
|
-
status: ResponseStatus.Fail,
|
|
122
|
-
requestId,
|
|
123
|
-
serviceMethod,
|
|
124
|
-
error,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
protected captureSendNotification(serviceMethod: string, args: any[]): void {
|
|
129
|
-
if (!this.capturer) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
this.capture({ type: MessageType.SendNotification, serviceMethod, arguments: args });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
40
|
listen(connection: T): void {
|
|
136
41
|
this.connection = connection;
|
|
42
|
+
this._disposables.add(this.connection);
|
|
137
43
|
this.bindMethods(this.registry.methods());
|
|
138
44
|
|
|
139
45
|
connection.listen();
|
|
@@ -145,9 +51,7 @@ export abstract class ProxyBase<T extends IBaseConnection> {
|
|
|
145
51
|
}
|
|
146
52
|
|
|
147
53
|
dispose(): void {
|
|
148
|
-
|
|
149
|
-
this.connection.dispose();
|
|
150
|
-
}
|
|
54
|
+
this._disposables.dispose();
|
|
151
55
|
}
|
|
152
56
|
|
|
153
57
|
public abstract invoke(prop: string, ...args: any[]): Promise<any>;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { Capturer } from '../../capturer';
|
|
3
2
|
import { METHOD_NOT_REGISTERED } from '../../constants';
|
|
4
3
|
|
|
5
4
|
import { ProxyBase } from './base';
|
|
6
5
|
|
|
6
|
+
import type { MessageConnection } from '@opensumi/vscode-jsonrpc';
|
|
7
|
+
|
|
7
8
|
interface IRPCResult {
|
|
8
9
|
error: boolean;
|
|
9
10
|
data: any;
|
|
@@ -11,12 +12,13 @@ interface IRPCResult {
|
|
|
11
12
|
|
|
12
13
|
export class ProxyJson extends ProxyBase<MessageConnection> {
|
|
13
14
|
protected engine = 'json' as const;
|
|
15
|
+
protected capturer = this._disposables.add(new Capturer(this.engine));
|
|
14
16
|
|
|
15
17
|
protected bindMethods(methods: string[]): void {
|
|
16
18
|
for (const method of methods) {
|
|
17
19
|
if (method.startsWith('on')) {
|
|
18
20
|
this.connection.onNotification(method, async (...args: any[]) => {
|
|
19
|
-
this.captureOnNotification(method, args);
|
|
21
|
+
this.capturer.captureOnNotification('_', method, args);
|
|
20
22
|
try {
|
|
21
23
|
await this.registry.invoke(method, ...this.serializeArguments(args));
|
|
22
24
|
} catch (e) {
|
|
@@ -26,19 +28,19 @@ export class ProxyJson extends ProxyBase<MessageConnection> {
|
|
|
26
28
|
} else {
|
|
27
29
|
this.connection.onRequest(method, async (...args: any[]) => {
|
|
28
30
|
const requestId = this.nextRequestId();
|
|
29
|
-
this.captureOnRequest(requestId, method, args);
|
|
31
|
+
this.capturer.captureOnRequest(requestId, method, args);
|
|
30
32
|
|
|
31
33
|
try {
|
|
32
34
|
const result = await this.registry.invoke(method, ...this.serializeArguments(args));
|
|
33
35
|
|
|
34
|
-
this.captureOnRequestResult(requestId, method, result);
|
|
36
|
+
this.capturer.captureOnRequestResult(requestId, method, result);
|
|
35
37
|
|
|
36
38
|
return {
|
|
37
39
|
error: false,
|
|
38
40
|
data: result,
|
|
39
41
|
};
|
|
40
42
|
} catch (e) {
|
|
41
|
-
this.captureOnRequestFail(requestId, method, e);
|
|
43
|
+
this.capturer.captureOnRequestFail(requestId, method, e);
|
|
42
44
|
|
|
43
45
|
return {
|
|
44
46
|
error: true,
|
|
@@ -63,7 +65,7 @@ export class ProxyJson extends ProxyBase<MessageConnection> {
|
|
|
63
65
|
|
|
64
66
|
// 调用方法为 on 开头时,作为单项通知
|
|
65
67
|
if (prop.startsWith('on')) {
|
|
66
|
-
this.captureSendNotification(prop, args);
|
|
68
|
+
this.capturer.captureSendNotification('_', prop, args);
|
|
67
69
|
if (isSingleArray) {
|
|
68
70
|
this.connection.sendNotification(prop, [...args]);
|
|
69
71
|
} else {
|
|
@@ -81,7 +83,7 @@ export class ProxyJson extends ProxyBase<MessageConnection> {
|
|
|
81
83
|
requestResult = this.connection.sendRequest(prop, ...args) as Promise<any>;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
this.captureSendRequest(requestId, prop, args);
|
|
86
|
+
this.capturer.captureSendRequest(requestId, prop, args);
|
|
85
87
|
|
|
86
88
|
const result: IRPCResult = await requestResult;
|
|
87
89
|
|
|
@@ -91,10 +93,10 @@ export class ProxyJson extends ProxyBase<MessageConnection> {
|
|
|
91
93
|
error.stack = result.data.stack;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
this.captureSendRequestFail(requestId, prop, result.data);
|
|
96
|
+
this.capturer.captureSendRequestFail(requestId, prop, result.data);
|
|
95
97
|
throw error;
|
|
96
98
|
} else {
|
|
97
|
-
this.captureSendRequestResult(requestId, prop, result.data);
|
|
99
|
+
this.capturer.captureSendRequestResult(requestId, prop, result.data);
|
|
98
100
|
return result.data;
|
|
99
101
|
}
|
|
100
102
|
}
|
|
@@ -128,11 +130,11 @@ export class ProxyJson extends ProxyBase<MessageConnection> {
|
|
|
128
130
|
connection.onRequest((method) => {
|
|
129
131
|
if (!this.registry.has(method)) {
|
|
130
132
|
const requestId = this.nextRequestId();
|
|
131
|
-
this.captureOnRequest(requestId, method, []);
|
|
133
|
+
this.capturer.captureOnRequest(requestId, method, []);
|
|
132
134
|
const result = {
|
|
133
135
|
data: METHOD_NOT_REGISTERED,
|
|
134
136
|
};
|
|
135
|
-
this.captureOnRequestFail(requestId, method, result.data);
|
|
137
|
+
this.capturer.captureOnRequestFail(requestId, method, result.data);
|
|
136
138
|
return result;
|
|
137
139
|
}
|
|
138
140
|
});
|