@opensumi/ide-connection 2.21.13 → 2.22.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.
@@ -0,0 +1,377 @@
1
+ import { CancellationToken, CancellationTokenSource, Deferred, Event, Uri } from '@opensumi/ide-core-common';
2
+ // Uri: vscode 中的 uri
3
+ // URI: 在 vscode 中的 uri 基础上包装了一些基础方法
4
+
5
+ export enum RPCProtocolEnv {
6
+ MAIN,
7
+ EXT,
8
+ }
9
+
10
+ export interface SerializedError {
11
+ readonly $isError: true;
12
+ readonly name: string;
13
+ readonly message: string;
14
+ readonly stack: string;
15
+ }
16
+
17
+ export function transformErrorForSerialization(error: Error): SerializedError;
18
+ export function transformErrorForSerialization(error: any): any;
19
+ export function transformErrorForSerialization(error: any): any {
20
+ if (error instanceof Error) {
21
+ const { name, message } = error;
22
+ const stack: string = (error as any).stacktrace || (error as any).stack;
23
+ return {
24
+ $isError: true,
25
+ name,
26
+ message,
27
+ stack,
28
+ };
29
+ }
30
+
31
+ return error;
32
+ }
33
+
34
+ export interface IProxyIdentifier {
35
+ serviceId: string;
36
+ countId: number;
37
+ }
38
+
39
+ export class ProxyIdentifier<T = any> {
40
+ public static count = 0;
41
+
42
+ public readonly serviceId: string;
43
+ public readonly countId: number;
44
+ constructor(serviceId: string) {
45
+ this.serviceId = serviceId;
46
+ this.countId = ++ProxyIdentifier.count;
47
+ }
48
+ }
49
+
50
+ export function createExtHostContextProxyIdentifier<T>(serviceId: string): ProxyIdentifier<T> {
51
+ const identifier = new ProxyIdentifier<T>(serviceId);
52
+ return identifier;
53
+ }
54
+ export function createMainContextProxyIdentifier<T>(identifier: string): ProxyIdentifier<T> {
55
+ const result = new ProxyIdentifier<T>(identifier);
56
+ return result;
57
+ }
58
+ export interface IMessagePassingProtocol {
59
+ send(msg): void;
60
+ onMessage: Event<string>;
61
+ }
62
+
63
+ const enum MessageType {
64
+ Request = 1,
65
+ Reply = 2,
66
+ ReplyErr = 3,
67
+ Cancel = 4,
68
+ }
69
+
70
+ interface RequestMessage {
71
+ type: MessageType.Request;
72
+ id: string;
73
+ proxyId: string;
74
+ method: string;
75
+ args: any[];
76
+ }
77
+ interface CancelMessage {
78
+ type: MessageType.Cancel;
79
+ id: string;
80
+ }
81
+
82
+ interface ReplyMessage {
83
+ type: MessageType.Reply;
84
+ id: string;
85
+ res: any;
86
+ }
87
+
88
+ interface ErrorMessage {
89
+ type: MessageType.Cancel;
90
+ id: string;
91
+ res: SerializedError;
92
+ }
93
+
94
+ export namespace ObjectTransfer {
95
+ export function replacer(key: string | undefined, value: any) {
96
+ if (value) {
97
+ if (value.$mid === 1) {
98
+ const uri = Uri.revive(value);
99
+ return {
100
+ $type: 'VSCODE_URI',
101
+ data: uri.toString(),
102
+ };
103
+ } else if (value instanceof Uint8Array || value instanceof Uint32Array || value instanceof Uint16Array) {
104
+ return {
105
+ $type: 'Buffer',
106
+ data: Array.from(value),
107
+ };
108
+ } else if (value instanceof ArrayBuffer) {
109
+ return {
110
+ $type: 'Buffer',
111
+ data: Array.from(new Uint8Array(value)),
112
+ };
113
+ }
114
+ }
115
+
116
+ return value;
117
+ }
118
+ export function reviver(key: string | undefined, value: any) {
119
+ if (value && value.$type !== undefined && value.data !== undefined) {
120
+ if (value.$type === 'VSCODE_URI') {
121
+ return Uri.parse(value.data);
122
+ }
123
+ if (value.$type === 'Buffer') {
124
+ return Uint8Array.from(value.data);
125
+ }
126
+ }
127
+ return value;
128
+ }
129
+ }
130
+
131
+ export class MessageIO {
132
+ public static cancel(req: string, messageToSendHostId?: string): string {
133
+ return `{"type":${MessageType.Cancel},"id":"${req}"}`;
134
+ }
135
+
136
+ public static serializeRequest(callId: string, rpcId: string, method: string, args: any[]): string {
137
+ args = args.map((arg) => {
138
+ if (arg instanceof Error) {
139
+ // 处理 Error 类型的参数
140
+ // eslint-disable-next-line prefer-rest-params
141
+ const array = Array.prototype.slice.call(arguments) as any[];
142
+ array[0] = arg.stack;
143
+ return array.join('\n');
144
+ }
145
+ return arg;
146
+ });
147
+
148
+ return `{"type": ${
149
+ MessageType.Request
150
+ }, "id": "${callId}", "proxyId": "${rpcId}", "method": "${method}", "args": ${JSON.stringify(
151
+ args,
152
+ ObjectTransfer.replacer,
153
+ )}}`;
154
+ }
155
+ public static serializeReplyOK(callId: string, res: any, logger?: any): string {
156
+ if (typeof res === 'undefined') {
157
+ return `{"type": ${MessageType.Reply}, "id": "${callId}"}`;
158
+ } else {
159
+ try {
160
+ return `{"type": ${MessageType.Reply}, "id": "${callId}", "res": ${JSON.stringify(
161
+ res,
162
+ ObjectTransfer.replacer,
163
+ )}}`;
164
+ } catch (e) {
165
+ if (logger) {
166
+ logger.warn('res', res);
167
+ }
168
+ return `{"type": ${MessageType.Reply}, "id": "${callId}", "res": {}}`;
169
+ }
170
+ }
171
+ }
172
+ public static serializeReplyError(callId: string, error: Error, logger?: any): string {
173
+ try {
174
+ return `{"type": ${MessageType.ReplyErr}, "id": "${callId}", "res": ${JSON.stringify(
175
+ transformErrorForSerialization(error),
176
+ ObjectTransfer.replacer,
177
+ )}}`;
178
+ } catch (e) {
179
+ if (logger) {
180
+ logger.error('error', error);
181
+ }
182
+ return `{"type": ${MessageType.ReplyErr}, "id": "${callId}", "res": {}}`;
183
+ }
184
+ }
185
+ }
186
+
187
+ export const IRPCProtocol = Symbol('IRPCProtocol');
188
+ export interface IRPCProtocol {
189
+ getProxy<T>(proxyId: ProxyIdentifier<T>): T;
190
+ set<T>(identifier: ProxyIdentifier<T>, instance: T): T;
191
+ get<T>(identifier: ProxyIdentifier<T>): T;
192
+ }
193
+
194
+ function canceled(): Error {
195
+ const error = new Error('Canceled');
196
+ error.name = error.message;
197
+ return error;
198
+ }
199
+
200
+ export class RPCProtocol implements IRPCProtocol {
201
+ private readonly _protocol: IMessagePassingProtocol;
202
+ private readonly _locals: Map<string, any>;
203
+ private readonly _proxies: Map<string, any>;
204
+ private readonly _cancellationTokenSources: Map<string, CancellationTokenSource>;
205
+ private _lastMessageId: number;
206
+ private _pendingRPCReplies: Map<string, Deferred<any>>;
207
+ private logger;
208
+
209
+ constructor(connection: IMessagePassingProtocol, logger?: any) {
210
+ this._protocol = connection;
211
+ this._locals = new Map();
212
+ this._proxies = new Map();
213
+ this._pendingRPCReplies = new Map();
214
+ this._cancellationTokenSources = new Map();
215
+
216
+ this._lastMessageId = 0;
217
+ this.logger = logger || console;
218
+ this._protocol.onMessage((msg) => this._receiveOneMessage(msg));
219
+ }
220
+
221
+ public set<T>(identifier: ProxyIdentifier<T>, instance: any) {
222
+ this._locals.set(identifier.serviceId, instance);
223
+ return instance;
224
+ }
225
+
226
+ public get<T>(identifier: ProxyIdentifier<T>) {
227
+ return this._locals.get(identifier.serviceId);
228
+ }
229
+
230
+ public getProxy<T>(proxyId: ProxyIdentifier<T>) {
231
+ if (!this._proxies.has(proxyId.serviceId)) {
232
+ this._proxies.set(proxyId.serviceId, this._createProxy(proxyId.serviceId));
233
+ }
234
+
235
+ return this._proxies.get(proxyId.serviceId);
236
+ }
237
+
238
+ private _createProxy(rpcId: string) {
239
+ const handler = {
240
+ get: (target: any, name: string) => {
241
+ if (typeof name === 'symbol') {
242
+ return null;
243
+ }
244
+ if (!target[name] && name.charCodeAt(0) === 36) {
245
+ target[name] = (...myArgs: any[]) => this._remoteCall(rpcId, name, myArgs);
246
+ }
247
+
248
+ return target[name];
249
+ },
250
+ };
251
+
252
+ return new Proxy(Object.create(null), handler);
253
+ }
254
+
255
+ private _remoteCall(rpcId: string, methodName: string, args: any[]): Promise<any> {
256
+ const cancellationToken: CancellationToken | undefined =
257
+ args.length && CancellationToken.isCancellationToken(args[args.length - 1]) ? args.pop() : undefined;
258
+ if (cancellationToken && cancellationToken.isCancellationRequested) {
259
+ return Promise.reject(canceled());
260
+ }
261
+
262
+ const callId = String(++this._lastMessageId);
263
+ const result = new Deferred();
264
+
265
+ if (cancellationToken) {
266
+ args.push('add.cancellation.token');
267
+ cancellationToken.onCancellationRequested(() => this._protocol.send(MessageIO.cancel(callId)));
268
+ }
269
+ this._pendingRPCReplies.set(callId, result);
270
+ const msg = MessageIO.serializeRequest(callId, rpcId, methodName, args);
271
+
272
+ this._protocol.send(msg);
273
+ return result.promise;
274
+ }
275
+
276
+ private _receiveOneMessage(rawmsg: string): void {
277
+ const msg = JSON.parse(rawmsg, ObjectTransfer.reviver);
278
+ switch (msg.type) {
279
+ case MessageType.Request:
280
+ this._receiveRequest(msg);
281
+ break;
282
+ case MessageType.Reply:
283
+ this._receiveReply(msg);
284
+ break;
285
+ case MessageType.Cancel:
286
+ this._receiveCancel(msg);
287
+ break;
288
+ case MessageType.ReplyErr:
289
+ this._receiveError(msg);
290
+ break;
291
+ }
292
+ }
293
+ private _receiveCancel(msg: CancelMessage) {
294
+ const cancellationTokenSource = this._cancellationTokenSources.get(msg.id);
295
+ if (cancellationTokenSource) {
296
+ cancellationTokenSource.cancel();
297
+ }
298
+ }
299
+
300
+ private _receiveError(msg: ErrorMessage) {
301
+ const callId = msg.id;
302
+ if (!this._pendingRPCReplies.has(callId)) {
303
+ return;
304
+ }
305
+
306
+ const pendingReply = this._pendingRPCReplies.get(callId) as Deferred<any>;
307
+ this._pendingRPCReplies.delete(callId);
308
+
309
+ let err: any;
310
+ if (msg.res) {
311
+ if (msg.res.$isError) {
312
+ err = new Error();
313
+ err.name = msg.res.name;
314
+ err.message = msg.res.message;
315
+ err.stack = msg.res.stack;
316
+ } else {
317
+ err = msg.res;
318
+ }
319
+ }
320
+ pendingReply.reject(err);
321
+ }
322
+
323
+ private _receiveRequest(msg: RequestMessage): void {
324
+ const callId = msg.id;
325
+ const rpcId = msg.proxyId;
326
+ const method = msg.method;
327
+ const args = msg.args.map((arg) => (arg === null ? undefined : arg));
328
+
329
+ const addToken = args.length && args[args.length - 1] === 'add.cancellation.token' ? args.pop() : false;
330
+ if (addToken) {
331
+ const tokenSource = new CancellationTokenSource();
332
+ this._cancellationTokenSources.set(callId, tokenSource);
333
+ args.push(tokenSource.token);
334
+ }
335
+
336
+ const promise = this._invokeHandler(rpcId, method, args);
337
+ promise
338
+ .then((r) => {
339
+ this._protocol.send(MessageIO.serializeReplyOK(callId, r));
340
+ this._cancellationTokenSources.delete(callId);
341
+ })
342
+ .catch((err) => {
343
+ this._protocol.send(MessageIO.serializeReplyError(callId, err));
344
+ this._cancellationTokenSources.delete(callId);
345
+ });
346
+ }
347
+ private _invokeHandler(rpcId: string, methodName: string, args: any[]) {
348
+ try {
349
+ return this._doInvokeHandler(rpcId, methodName, args);
350
+ } catch (err) {
351
+ return Promise.reject(err);
352
+ }
353
+ }
354
+ private async _doInvokeHandler(rpcId: string, methodName: string, args: any[]): Promise<any> {
355
+ const actor = this._locals.get(rpcId);
356
+ if (!actor) {
357
+ throw new Error('Unknown actor ' + rpcId);
358
+ }
359
+ const method = await actor[methodName];
360
+ if (typeof method !== 'function') {
361
+ throw new Error('Unknown method ' + methodName + ' on actor ' + rpcId);
362
+ }
363
+
364
+ return method.apply(actor, args);
365
+ }
366
+ private _receiveReply(msg: ReplyMessage) {
367
+ const callId = msg.id;
368
+ if (!this._pendingRPCReplies.has(callId)) {
369
+ return;
370
+ }
371
+
372
+ const pendingReply = this._pendingRPCReplies.get(callId) as Deferred<any>;
373
+ this._pendingRPCReplies.delete(callId);
374
+
375
+ pendingReply.resolve(msg.res);
376
+ }
377
+ }
@@ -0,0 +1,44 @@
1
+ declare global {
2
+ interface Window {
3
+ __OPENSUMI_DEVTOOLS_GLOBAL_HOOK__: any;
4
+ }
5
+ }
6
+
7
+ export enum MessageType {
8
+ SendNotification = 'sendNotification',
9
+ SendRequest = 'sendRequest',
10
+ RequestResult = 'requestResult',
11
+ OnNotification = 'onNotification',
12
+ OnRequest = 'onRequest',
13
+ OnRequestResult = 'onRequestResult',
14
+ }
15
+
16
+ export enum ResponseStatus {
17
+ Success = 'success',
18
+ Fail = 'fail',
19
+ }
20
+
21
+ export interface ICapturedMessage {
22
+ type: MessageType;
23
+ serviceMethod: string;
24
+ arguments?: any;
25
+ requestId?: string;
26
+ status?: ResponseStatus;
27
+ data?: any;
28
+ error?: any;
29
+ }
30
+
31
+ export function stringify(obj: any): string {
32
+ return JSON.stringify(obj);
33
+ }
34
+
35
+ export function parse(input: string, reviver?: (this: any, key: string, value: any) => any): any {
36
+ return JSON.parse(input, reviver);
37
+ }
38
+
39
+ export function getCapturer() {
40
+ if (typeof window !== 'undefined' && window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.captureRPC) {
41
+ return window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.captureRPC;
42
+ }
43
+ return;
44
+ }
@@ -0,0 +1,144 @@
1
+ import { stringify } from './utils';
2
+
3
+ export interface IWebSocket {
4
+ send(content: string): void;
5
+ close(...args): void;
6
+ onMessage(cb: (data: any) => void): void;
7
+ onError(cb: (reason: any) => void): void;
8
+ onClose(cb: (code: number, reason: string) => void): void;
9
+ }
10
+
11
+ export interface ClientMessage {
12
+ kind: 'client';
13
+ clientId: string;
14
+ }
15
+ export interface HeartbeatMessage {
16
+ kind: 'heartbeat';
17
+ clientId: string;
18
+ }
19
+ export interface OpenMessage {
20
+ kind: 'open';
21
+ id: number;
22
+ path: string;
23
+ }
24
+ export interface ReadyMessage {
25
+ kind: 'ready';
26
+ id: number;
27
+ }
28
+ export interface DataMessage {
29
+ kind: 'data';
30
+ id: number;
31
+ content: string;
32
+ }
33
+ export interface CloseMessage {
34
+ kind: 'close';
35
+ id: number;
36
+ code: number;
37
+ reason: string;
38
+ }
39
+ export type ChannelMessage = HeartbeatMessage | ClientMessage | OpenMessage | ReadyMessage | DataMessage | CloseMessage;
40
+
41
+ export class WSChannel implements IWebSocket {
42
+ public id: number | string;
43
+ public channelPath: string;
44
+
45
+ private connectionSend: (content: string) => void;
46
+ private fireMessage: (data: any) => void;
47
+ private fireOpen: (id: number) => void;
48
+ public fireReOpen: () => void;
49
+ private fireClose: (code: number, reason: string) => void;
50
+
51
+ public messageConnection: any;
52
+
53
+ constructor(connectionSend: (content: string) => void, id?: number | string) {
54
+ this.connectionSend = connectionSend;
55
+ if (id) {
56
+ this.id = id;
57
+ }
58
+ }
59
+
60
+ public setConnectionSend(connectionSend: (content: string) => void) {
61
+ this.connectionSend = connectionSend;
62
+ }
63
+
64
+ // server
65
+ onMessage(cb: (data: any) => any) {
66
+ this.fireMessage = cb;
67
+ }
68
+ onOpen(cb: (id: number) => void) {
69
+ this.fireOpen = cb;
70
+ }
71
+ onReOpen(cb: () => void) {
72
+ this.fireReOpen = cb;
73
+ }
74
+ ready() {
75
+ this.connectionSend(
76
+ stringify({
77
+ kind: 'ready',
78
+ id: this.id,
79
+ }),
80
+ );
81
+ }
82
+ handleMessage(msg: ChannelMessage) {
83
+ if (msg.kind === 'ready' && this.fireOpen) {
84
+ this.fireOpen(msg.id);
85
+ } else if (msg.kind === 'data' && this.fireMessage) {
86
+ this.fireMessage(msg.content);
87
+ }
88
+ }
89
+
90
+ // client
91
+ open(path: string) {
92
+ this.channelPath = path;
93
+ this.connectionSend(
94
+ stringify({
95
+ kind: 'open',
96
+ id: this.id,
97
+ path,
98
+ }),
99
+ );
100
+ }
101
+ send(content: string) {
102
+ this.connectionSend(
103
+ stringify({
104
+ kind: 'data',
105
+ id: this.id,
106
+ content,
107
+ }),
108
+ );
109
+ }
110
+ onError() {}
111
+ close(code: number, reason: string) {
112
+ if (this.fireClose) {
113
+ this.fireClose(code, reason);
114
+ }
115
+ }
116
+ onClose(cb: (code: number, reason: string) => void) {
117
+ this.fireClose = cb;
118
+ }
119
+ }
120
+
121
+ export type MessageString = string & {
122
+ origin?: any;
123
+ };
124
+
125
+ /**
126
+ * 路径信息 ${pre}-${index}
127
+ */
128
+ export class ChildConnectPath {
129
+ public pathPre = 'child_connect-';
130
+
131
+ getConnectPath(index: number, clientId: string) {
132
+ return `${this.pathPre}${index + 1}`;
133
+ }
134
+
135
+ parseInfo(pathString: string) {
136
+ const list = pathString.split('-');
137
+
138
+ return {
139
+ pre: list[0],
140
+ index: list[1],
141
+ clientId: list[2],
142
+ };
143
+ }
144
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './common';