@celljs/rpc 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/cell.yml +8 -0
  4. package/lib/common/annotation/autorpc.d.ts +6 -0
  5. package/lib/common/annotation/autorpc.d.ts.map +1 -0
  6. package/lib/common/annotation/autorpc.js +19 -0
  7. package/lib/common/annotation/autorpc.js.map +1 -0
  8. package/lib/common/annotation/detached/autorpc.d.ts +3 -0
  9. package/lib/common/annotation/detached/autorpc.d.ts.map +1 -0
  10. package/lib/common/annotation/detached/autorpc.js +15 -0
  11. package/lib/common/annotation/detached/autorpc.js.map +1 -0
  12. package/lib/common/annotation/detached/index.d.ts +2 -0
  13. package/lib/common/annotation/detached/index.d.ts.map +1 -0
  14. package/lib/common/annotation/detached/index.js +18 -0
  15. package/lib/common/annotation/detached/index.js.map +1 -0
  16. package/lib/common/annotation/index.d.ts +3 -0
  17. package/lib/common/annotation/index.d.ts.map +1 -0
  18. package/lib/common/annotation/index.js +19 -0
  19. package/lib/common/annotation/index.js.map +1 -0
  20. package/lib/common/annotation/rpc.d.ts +20 -0
  21. package/lib/common/annotation/rpc.d.ts.map +1 -0
  22. package/lib/common/annotation/rpc.js +41 -0
  23. package/lib/common/annotation/rpc.js.map +1 -0
  24. package/lib/common/channal/channel-protocol.d.ts +52 -0
  25. package/lib/common/channal/channel-protocol.d.ts.map +1 -0
  26. package/lib/common/channal/channel-protocol.js +38 -0
  27. package/lib/common/channal/channel-protocol.js.map +1 -0
  28. package/lib/common/channal/http-channel.d.ts +9 -0
  29. package/lib/common/channal/http-channel.d.ts.map +1 -0
  30. package/lib/common/channal/http-channel.js +26 -0
  31. package/lib/common/channal/http-channel.js.map +1 -0
  32. package/lib/common/channal/index.d.ts +3 -0
  33. package/lib/common/channal/index.d.ts.map +1 -0
  34. package/lib/common/channal/index.js +19 -0
  35. package/lib/common/channal/index.js.map +1 -0
  36. package/lib/common/constants.d.ts +2 -0
  37. package/lib/common/constants.d.ts.map +1 -0
  38. package/lib/common/constants.js +5 -0
  39. package/lib/common/constants.js.map +1 -0
  40. package/lib/common/converter/converter-protocol.d.ts +7 -0
  41. package/lib/common/converter/converter-protocol.d.ts.map +1 -0
  42. package/lib/common/converter/converter-protocol.js +6 -0
  43. package/lib/common/converter/converter-protocol.js.map +1 -0
  44. package/lib/common/converter/index.d.ts +2 -0
  45. package/lib/common/converter/index.d.ts.map +1 -0
  46. package/lib/common/converter/index.js +18 -0
  47. package/lib/common/converter/index.js.map +1 -0
  48. package/lib/common/endpoint/endpoint-protocol.d.ts +5 -0
  49. package/lib/common/endpoint/endpoint-protocol.d.ts.map +1 -0
  50. package/lib/common/endpoint/endpoint-protocol.js +5 -0
  51. package/lib/common/endpoint/endpoint-protocol.js.map +1 -0
  52. package/lib/common/endpoint/endpoint-resolver.d.ts +13 -0
  53. package/lib/common/endpoint/endpoint-resolver.d.ts.map +1 -0
  54. package/lib/common/endpoint/endpoint-resolver.js +61 -0
  55. package/lib/common/endpoint/endpoint-resolver.js.map +1 -0
  56. package/lib/common/endpoint/index.d.ts +3 -0
  57. package/lib/common/endpoint/index.d.ts.map +1 -0
  58. package/lib/common/endpoint/index.js +19 -0
  59. package/lib/common/endpoint/index.js.map +1 -0
  60. package/lib/common/error/error.d.ts +6 -0
  61. package/lib/common/error/error.d.ts.map +1 -0
  62. package/lib/common/error/error.js +12 -0
  63. package/lib/common/error/error.js.map +1 -0
  64. package/lib/common/error/index.d.ts +2 -0
  65. package/lib/common/error/index.d.ts.map +1 -0
  66. package/lib/common/error/index.js +18 -0
  67. package/lib/common/error/index.js.map +1 -0
  68. package/lib/common/factory/connection-factory.d.ts +10 -0
  69. package/lib/common/factory/connection-factory.d.ts.map +1 -0
  70. package/lib/common/factory/connection-factory.js +28 -0
  71. package/lib/common/factory/connection-factory.js.map +1 -0
  72. package/lib/common/factory/index.d.ts +5 -0
  73. package/lib/common/factory/index.d.ts.map +1 -0
  74. package/lib/common/factory/index.js +21 -0
  75. package/lib/common/factory/index.js.map +1 -0
  76. package/lib/common/factory/proxy-factory.d.ts +57 -0
  77. package/lib/common/factory/proxy-factory.d.ts.map +1 -0
  78. package/lib/common/factory/proxy-factory.js +182 -0
  79. package/lib/common/factory/proxy-factory.js.map +1 -0
  80. package/lib/common/factory/reader.d.ts +16 -0
  81. package/lib/common/factory/reader.d.ts.map +1 -0
  82. package/lib/common/factory/reader.js +48 -0
  83. package/lib/common/factory/reader.js.map +1 -0
  84. package/lib/common/factory/writer.d.ts +10 -0
  85. package/lib/common/factory/writer.d.ts.map +1 -0
  86. package/lib/common/factory/writer.js +25 -0
  87. package/lib/common/factory/writer.js.map +1 -0
  88. package/lib/common/handler/connection-handler.d.ts +7 -0
  89. package/lib/common/handler/connection-handler.d.ts.map +1 -0
  90. package/lib/common/handler/connection-handler.js +8 -0
  91. package/lib/common/handler/connection-handler.js.map +1 -0
  92. package/lib/common/handler/handler-protocol.d.ts +7 -0
  93. package/lib/common/handler/handler-protocol.d.ts.map +1 -0
  94. package/lib/common/handler/handler-protocol.js +5 -0
  95. package/lib/common/handler/handler-protocol.js.map +1 -0
  96. package/lib/common/handler/index.d.ts +3 -0
  97. package/lib/common/handler/index.d.ts.map +1 -0
  98. package/lib/common/handler/index.js +19 -0
  99. package/lib/common/handler/index.js.map +1 -0
  100. package/lib/common/index.d.ts +12 -0
  101. package/lib/common/index.d.ts.map +1 -0
  102. package/lib/common/index.js +28 -0
  103. package/lib/common/index.js.map +1 -0
  104. package/lib/common/module.d.ts +4 -0
  105. package/lib/common/module.d.ts.map +1 -0
  106. package/lib/common/module.js +22 -0
  107. package/lib/common/module.js.map +1 -0
  108. package/lib/common/processor/client-config-processor.d.ts +6 -0
  109. package/lib/common/processor/client-config-processor.d.ts.map +1 -0
  110. package/lib/common/processor/client-config-processor.js +20 -0
  111. package/lib/common/processor/client-config-processor.js.map +1 -0
  112. package/lib/common/processor/index.d.ts +3 -0
  113. package/lib/common/processor/index.d.ts.map +1 -0
  114. package/lib/common/processor/index.js +19 -0
  115. package/lib/common/processor/index.js.map +1 -0
  116. package/lib/common/processor/processor-protocol.d.ts +6 -0
  117. package/lib/common/processor/processor-protocol.d.ts.map +1 -0
  118. package/lib/common/processor/processor-protocol.js +5 -0
  119. package/lib/common/processor/processor-protocol.js.map +1 -0
  120. package/lib/common/proxy/delegating-http-proxy-creator.d.ts +24 -0
  121. package/lib/common/proxy/delegating-http-proxy-creator.d.ts.map +1 -0
  122. package/lib/common/proxy/delegating-http-proxy-creator.js +89 -0
  123. package/lib/common/proxy/delegating-http-proxy-creator.js.map +1 -0
  124. package/lib/common/proxy/http-proxy-creator.d.ts +43 -0
  125. package/lib/common/proxy/http-proxy-creator.d.ts.map +1 -0
  126. package/lib/common/proxy/http-proxy-creator.js +137 -0
  127. package/lib/common/proxy/http-proxy-creator.js.map +1 -0
  128. package/lib/common/proxy/index.d.ts +5 -0
  129. package/lib/common/proxy/index.d.ts.map +1 -0
  130. package/lib/common/proxy/index.js +21 -0
  131. package/lib/common/proxy/index.js.map +1 -0
  132. package/lib/common/proxy/proxy-protocol.d.ts +22 -0
  133. package/lib/common/proxy/proxy-protocol.d.ts.map +1 -0
  134. package/lib/common/proxy/proxy-protocol.js +6 -0
  135. package/lib/common/proxy/proxy-protocol.js.map +1 -0
  136. package/lib/common/proxy/proxy-provider.d.ts +10 -0
  137. package/lib/common/proxy/proxy-provider.d.ts.map +1 -0
  138. package/lib/common/proxy/proxy-provider.js +43 -0
  139. package/lib/common/proxy/proxy-provider.js.map +1 -0
  140. package/lib/common/utils/converter-util.d.ts +8 -0
  141. package/lib/common/utils/converter-util.d.ts.map +1 -0
  142. package/lib/common/utils/converter-util.js +32 -0
  143. package/lib/common/utils/converter-util.js.map +1 -0
  144. package/lib/common/utils/index.d.ts +4 -0
  145. package/lib/common/utils/index.d.ts.map +1 -0
  146. package/lib/common/utils/index.js +20 -0
  147. package/lib/common/utils/index.js.map +1 -0
  148. package/lib/common/utils/logger.d.ts +9 -0
  149. package/lib/common/utils/logger.d.ts.map +1 -0
  150. package/lib/common/utils/logger.js +22 -0
  151. package/lib/common/utils/logger.js.map +1 -0
  152. package/lib/common/utils/rpc-util.d.ts +13 -0
  153. package/lib/common/utils/rpc-util.d.ts.map +1 -0
  154. package/lib/common/utils/rpc-util.js +46 -0
  155. package/lib/common/utils/rpc-util.js.map +1 -0
  156. package/lib/node/channel/channel-manager.d.ts +10 -0
  157. package/lib/node/channel/channel-manager.d.ts.map +1 -0
  158. package/lib/node/channel/channel-manager.js +59 -0
  159. package/lib/node/channel/channel-manager.js.map +1 -0
  160. package/lib/node/channel/channel-middleware.d.ts +8 -0
  161. package/lib/node/channel/channel-middleware.d.ts.map +1 -0
  162. package/lib/node/channel/channel-middleware.js +40 -0
  163. package/lib/node/channel/channel-middleware.js.map +1 -0
  164. package/lib/node/channel/channel-protocol.d.ts +13 -0
  165. package/lib/node/channel/channel-protocol.d.ts.map +1 -0
  166. package/lib/node/channel/channel-protocol.js +9 -0
  167. package/lib/node/channel/channel-protocol.js.map +1 -0
  168. package/lib/node/channel/channel-strategy.d.ts +10 -0
  169. package/lib/node/channel/channel-strategy.d.ts.map +1 -0
  170. package/lib/node/channel/channel-strategy.js +64 -0
  171. package/lib/node/channel/channel-strategy.js.map +1 -0
  172. package/lib/node/channel/index.d.ts +5 -0
  173. package/lib/node/channel/index.d.ts.map +1 -0
  174. package/lib/node/channel/index.js +21 -0
  175. package/lib/node/channel/index.js.map +1 -0
  176. package/lib/node/handler/handler-adapter.d.ts +14 -0
  177. package/lib/node/handler/handler-adapter.d.ts.map +1 -0
  178. package/lib/node/handler/handler-adapter.js +55 -0
  179. package/lib/node/handler/handler-adapter.js.map +1 -0
  180. package/lib/node/handler/handler-protocol.d.ts +2 -0
  181. package/lib/node/handler/handler-protocol.d.ts.map +1 -0
  182. package/lib/node/handler/handler-protocol.js +5 -0
  183. package/lib/node/handler/handler-protocol.js.map +1 -0
  184. package/lib/node/handler/index.d.ts +3 -0
  185. package/lib/node/handler/index.d.ts.map +1 -0
  186. package/lib/node/handler/index.js +19 -0
  187. package/lib/node/handler/index.js.map +1 -0
  188. package/lib/node/index.d.ts +3 -0
  189. package/lib/node/index.d.ts.map +1 -0
  190. package/lib/node/index.js +19 -0
  191. package/lib/node/index.js.map +1 -0
  192. package/lib/node/module.d.ts +4 -0
  193. package/lib/node/module.d.ts.map +1 -0
  194. package/lib/node/module.js +6 -0
  195. package/lib/node/module.js.map +1 -0
  196. package/lib/package.spec.d.ts +1 -0
  197. package/lib/package.spec.d.ts.map +1 -0
  198. package/lib/package.spec.js +4 -0
  199. package/lib/package.spec.js.map +1 -0
  200. package/package.json +45 -0
  201. package/src/common/annotation/autorpc.ts +17 -0
  202. package/src/common/annotation/detached/autorpc.ts +12 -0
  203. package/src/common/annotation/detached/index.ts +1 -0
  204. package/src/common/annotation/index.ts +2 -0
  205. package/src/common/annotation/rpc.ts +41 -0
  206. package/src/common/channal/channel-protocol.ts +82 -0
  207. package/src/common/channal/http-channel.ts +30 -0
  208. package/src/common/channal/index.ts +2 -0
  209. package/src/common/constants.ts +1 -0
  210. package/src/common/converter/converter-protocol.ts +8 -0
  211. package/src/common/converter/index.ts +1 -0
  212. package/src/common/endpoint/endpoint-protocol.ts +5 -0
  213. package/src/common/endpoint/endpoint-resolver.ts +46 -0
  214. package/src/common/endpoint/index.ts +2 -0
  215. package/src/common/error/error.ts +7 -0
  216. package/src/common/error/index.ts +1 -0
  217. package/src/common/factory/connection-factory.ts +21 -0
  218. package/src/common/factory/index.ts +4 -0
  219. package/src/common/factory/proxy-factory.ts +219 -0
  220. package/src/common/factory/reader.ts +50 -0
  221. package/src/common/factory/writer.ts +26 -0
  222. package/src/common/handler/connection-handler.ts +7 -0
  223. package/src/common/handler/handler-protocol.ts +8 -0
  224. package/src/common/handler/index.ts +2 -0
  225. package/src/common/index.ts +11 -0
  226. package/src/common/module.ts +21 -0
  227. package/src/common/processor/client-config-processor.ts +12 -0
  228. package/src/common/processor/index.ts +2 -0
  229. package/src/common/processor/processor-protocol.ts +7 -0
  230. package/src/common/proxy/delegating-http-proxy-creator.ts +68 -0
  231. package/src/common/proxy/http-proxy-creator.ts +179 -0
  232. package/src/common/proxy/index.ts +5 -0
  233. package/src/common/proxy/proxy-protocol.ts +26 -0
  234. package/src/common/proxy/proxy-provider.ts +29 -0
  235. package/src/common/utils/converter-util.ts +29 -0
  236. package/src/common/utils/index.ts +3 -0
  237. package/src/common/utils/logger.ts +23 -0
  238. package/src/common/utils/rpc-util.ts +46 -0
  239. package/src/node/channel/channel-manager.ts +44 -0
  240. package/src/node/channel/channel-middleware.ts +27 -0
  241. package/src/node/channel/channel-protocol.ts +21 -0
  242. package/src/node/channel/channel-strategy.ts +56 -0
  243. package/src/node/channel/index.ts +4 -0
  244. package/src/node/handler/handler-adapter.ts +38 -0
  245. package/src/node/handler/handler-protocol.ts +1 -0
  246. package/src/node/handler/index.ts +2 -0
  247. package/src/node/index.ts +2 -0
  248. package/src/node/module.ts +4 -0
  249. package/src/package.spec.ts +4 -0
@@ -0,0 +1,219 @@
1
+ import { MessageConnection, ResponseError, Emitter, Event } from 'vscode-jsonrpc';
2
+ import { ApplicationError, Disposable, Logger, getTargetClass, getPropertyNames } from '@celljs/core';
3
+ import { ErrorConverter } from '../converter';
4
+ import { ConnectionHandler } from '../handler';
5
+ import { PipeManager } from '@celljs/web';
6
+
7
+ export type JsonRpcServer<Client> = Disposable & {
8
+ /**
9
+ * If this server is a proxy to a remote server then
10
+ * a client is used as a local object
11
+ * to handle JSON-RPC messages from the remote server.
12
+ */
13
+ setClient(client: Client | undefined): void;
14
+ };
15
+
16
+ export interface JsonRpcConnectionEventEmitter {
17
+ readonly onDidOpenConnection: Event<void> ;
18
+ readonly onDidCloseConnection: Event<void>;
19
+ }
20
+ export type JsonRpcProxy<T> = T & JsonRpcConnectionEventEmitter;
21
+
22
+ export class JsonRpcConnectionHandler<T extends object> implements ConnectionHandler {
23
+ constructor(
24
+ readonly path: string,
25
+ readonly targetFactory: (proxy: JsonRpcProxy<T>) => any,
26
+ readonly errorConverters: ErrorConverter[],
27
+ readonly pipeManager: PipeManager,
28
+ readonly logger: Logger
29
+ ) { }
30
+
31
+ onConnection(connection: MessageConnection): void {
32
+ const factory = new JsonRpcProxyFactory<T>(undefined, this.errorConverters, this.pipeManager, this.logger);
33
+ const proxy = factory.createProxy();
34
+ factory.target = this.targetFactory(proxy);
35
+ factory.listen(connection);
36
+ }
37
+ }
38
+
39
+ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
40
+
41
+ private static SET_CLIENT = 'setClient';
42
+ private static ON_DID_OPEN_CONNECTION = 'onDidOpenConnection';
43
+ private static ON_DID_CLOSE_CONNECTION = 'onDidCloseConnection';
44
+ private readonly onDidOpenConnectionEmitter = new Emitter<void>();
45
+ private readonly onDidCloseConnectionEmitter = new Emitter<void>();
46
+ protected internalMethods: Map<string, () => any>;
47
+
48
+ protected connectionPromiseResolve: (connection: MessageConnection) => void;
49
+ protected connectionPromise: Promise<MessageConnection>;
50
+
51
+ constructor(
52
+ public target?: any,
53
+ protected errorConverters?: ErrorConverter[],
54
+ protected pipeManager?: PipeManager,
55
+ protected logger?: Logger) {
56
+ this.waitForConnection();
57
+ }
58
+
59
+ protected waitForConnection(): void {
60
+ this.connectionPromise = new Promise(resolve =>
61
+ this.connectionPromiseResolve = resolve
62
+ );
63
+ this.connectionPromise.then(connection => {
64
+ connection.onClose(() =>
65
+ this.onDidCloseConnectionEmitter.fire(undefined)
66
+ );
67
+ this.onDidOpenConnectionEmitter.fire(undefined);
68
+ });
69
+ }
70
+
71
+ protected getRequestHander(prop: string) {
72
+ return (...args: any[]) => this.onRequest(prop, ...args);
73
+ }
74
+
75
+ protected getNotificationHander(prop: string) {
76
+ return (...args: any[]) => this.onNotification(prop, ...args);
77
+ }
78
+
79
+ protected registerMethods(connection: MessageConnection) {
80
+ if (this.target) {
81
+ for (const prop of getPropertyNames(this.target)) {
82
+ if (typeof this.target[prop] === 'function') {
83
+ connection.onRequest(prop, this.getRequestHander(prop));
84
+ connection.onNotification(prop, this.getNotificationHander(prop));
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ listen(connection: MessageConnection) {
91
+ this.registerMethods(connection);
92
+ connection.onDispose(() => this.waitForConnection());
93
+ connection.listen();
94
+ this.connectionPromiseResolve(connection);
95
+ }
96
+
97
+ protected async onRequest(method: string, ...args: any[]): Promise<any> {
98
+ const now = Date.now();
99
+ const message = `${getTargetClass(this.target).name}.${method}`;
100
+ // eslint-disable-next-line no-unused-expressions
101
+ this.logger?.info(`starting ${message}`);
102
+ try {
103
+ // eslint-disable-next-line no-unused-expressions
104
+ await this.pipeManager?.apply({ target: this.target, method: method }, args);
105
+
106
+ return await this.target[method](...args);
107
+ } catch (error) {
108
+ const e = this.serializeError(error);
109
+ if (e instanceof ResponseError) {
110
+ throw e;
111
+ }
112
+ const reason = e.message || '';
113
+ const stack = e.stack || '';
114
+ // eslint-disable-next-line no-unused-expressions
115
+ this.logger?.error(`Request ${method} failed with error: ${reason}`, stack);
116
+ throw e;
117
+ } finally {
118
+ // eslint-disable-next-line no-unused-expressions
119
+ this.logger?.info(`ending ${message}, cost ${Date.now() - now}ms`);
120
+
121
+ }
122
+ }
123
+
124
+ protected onNotification(method: string, ...args: any[]): void {
125
+ this.target[method](...args);
126
+ }
127
+
128
+ createProxy(): JsonRpcProxy<T> {
129
+ const proxy = new Proxy<T>(this as any, this);
130
+ return proxy as any;
131
+ }
132
+
133
+ get(target: T, p: PropertyKey, receiver: any): any {
134
+ const method = this.getInternalMethod(p.toString());
135
+ if (method) {
136
+ return method;
137
+ }
138
+ return this.createProxyMethod(p.toString());
139
+ }
140
+
141
+ protected getInternalMethod(method: string) {
142
+ if (method === JsonRpcProxyFactory.SET_CLIENT) {
143
+ return (client: any) => this.target = client;
144
+ }
145
+ if (method === JsonRpcProxyFactory.ON_DID_OPEN_CONNECTION) {
146
+ return this.onDidOpenConnectionEmitter.event;
147
+ }
148
+ if (method === JsonRpcProxyFactory.ON_DID_CLOSE_CONNECTION) {
149
+ return this.onDidCloseConnectionEmitter.event;
150
+ }
151
+ }
152
+
153
+ protected createProxyMethod(method: string) {
154
+ if (method === 'then') {
155
+ return undefined;
156
+ }
157
+ const isNotify = this.isNotification(method);
158
+ return async (...args: any[]) => {
159
+ const capturedError = new Error(`Request '${method}' failed`);
160
+ const connection = await this.connectionPromise;
161
+ if (isNotify) {
162
+ connection.sendNotification(method, ...args);
163
+ } else {
164
+ try {
165
+ return await connection.sendRequest(method, ...args) as Promise<any>;
166
+ } catch (err) {
167
+ throw this.deserializeError(capturedError, err);
168
+ }
169
+ }
170
+ };
171
+ }
172
+
173
+ protected isNotification(p: PropertyKey): boolean {
174
+ return p.toString().startsWith('notify') || p.toString().startsWith('on');
175
+ }
176
+
177
+ protected serializeError(e: any): any {
178
+ if (this.errorConverters) {
179
+ for (const converter of this.errorConverters) {
180
+ const serialized = converter.serialize(e);
181
+ if (serialized) {
182
+ e = serialized;
183
+ break;
184
+ }
185
+ }
186
+ }
187
+ if (ApplicationError.is(e)) {
188
+ return new ResponseError(e.code, '',
189
+ Object.assign({ kind: 'application' }, e.toJson())
190
+ );
191
+ }
192
+ return e;
193
+ }
194
+ protected deserializeError(capturedError: Error, e: any): any {
195
+ if (e instanceof ResponseError) {
196
+ const capturedStack = capturedError.stack || '';
197
+ if (e.data && e.data.kind === 'application') {
198
+ const { stack, data, message } = e.data;
199
+ e = ApplicationError.fromJson(e.code, {
200
+ message: message || capturedError.message,
201
+ data,
202
+ stack: `${capturedStack}\nCaused by: ${stack}`
203
+ });
204
+ } else {
205
+ e.stack = capturedStack;
206
+ }
207
+ }
208
+ if (this.errorConverters) {
209
+ for (const converter of this.errorConverters) {
210
+ const deserialized = converter.deserialize(e);
211
+ if (deserialized) {
212
+ return deserialized;
213
+ }
214
+ }
215
+ }
216
+ return e;
217
+ }
218
+
219
+ }
@@ -0,0 +1,50 @@
1
+ import { DataCallback, AbstractMessageReader, Disposable } from 'vscode-jsonrpc';
2
+ import { Channel } from '../channal/channel-protocol';
3
+
4
+ export class ChannelMessageReader extends AbstractMessageReader {
5
+
6
+ protected state: 'initial' | 'listening' | 'closed' = 'initial';
7
+ protected callback: DataCallback | undefined;
8
+ protected readonly events: { message?: any, error?: any }[] = [];
9
+
10
+ constructor(protected readonly channel: Channel) {
11
+ super();
12
+ this.channel.onMessage(message =>
13
+ this.readMessage(message)
14
+ );
15
+ }
16
+
17
+ listen(callback: DataCallback): Disposable {
18
+ if (this.state === 'initial') {
19
+ this.state = 'listening';
20
+ this.callback = callback;
21
+ while (this.events.length !== 0) {
22
+ const event = this.events.pop()!;
23
+ if (event.message) {
24
+ this.readMessage(event.message);
25
+ } else if (event.error) {
26
+ this.fireError(event.error);
27
+ }
28
+ }
29
+ }
30
+ return Disposable.create(() => {});
31
+ }
32
+
33
+ protected readMessage(message: any): void {
34
+ if (this.state === 'initial') {
35
+ this.events.splice(0, 0, { message });
36
+ } else if (this.state === 'listening') {
37
+ const data = JSON.parse(message);
38
+ this.callback!(data);
39
+ }
40
+ }
41
+
42
+ protected override fireError(error: any): void {
43
+ if (this.state === 'initial') {
44
+ this.events.splice(0, 0, { error });
45
+ } else if (this.state === 'listening') {
46
+ super.fireError(error);
47
+ }
48
+ }
49
+
50
+ }
@@ -0,0 +1,26 @@
1
+ import { Message, AbstractMessageWriter } from 'vscode-jsonrpc';
2
+ import { Channel } from '../channal';
3
+
4
+ export class ChannelMessageWriter extends AbstractMessageWriter {
5
+
6
+ protected errorCount = 0;
7
+
8
+ constructor(protected readonly channel: Channel) {
9
+ super();
10
+ }
11
+
12
+ async write(msg: Message): Promise<void> {
13
+ try {
14
+ const content = JSON.stringify(msg);
15
+ this.channel.send(content);
16
+ } catch (e) {
17
+ this.errorCount++;
18
+ this.fireError(e, msg, this.errorCount);
19
+ }
20
+ }
21
+
22
+ end() {
23
+
24
+ }
25
+
26
+ }
@@ -0,0 +1,7 @@
1
+ import { MessageConnection } from 'vscode-jsonrpc';
2
+ import { ConnectionHandler } from './handler-protocol';
3
+
4
+ export class NoOpConnectionHandler implements ConnectionHandler {
5
+ readonly path: string;
6
+ onConnection(connection: MessageConnection): void {}
7
+ }
@@ -0,0 +1,8 @@
1
+ import { MessageConnection } from 'vscode-jsonrpc';
2
+
3
+ export const ConnectionHandler = Symbol('ConnectionHandler');
4
+
5
+ export interface ConnectionHandler {
6
+ readonly path: string;
7
+ onConnection(connection: MessageConnection): void;
8
+ }
@@ -0,0 +1,2 @@
1
+ export * from './connection-handler';
2
+ export * from './handler-protocol';
@@ -0,0 +1,11 @@
1
+ export * from './annotation';
2
+ export * from './channal';
3
+ export * from './endpoint';
4
+ export * from './error';
5
+ export * from './factory';
6
+ export * from './handler';
7
+ export * from './proxy';
8
+ export * from './utils';
9
+ export * from './constants';
10
+ export * from './converter';
11
+ export * from './processor';
@@ -0,0 +1,21 @@
1
+ import { autoBind } from '@celljs/core';
2
+ import { ProxyProvider } from './proxy';
3
+ import { ID_KEY, RPC } from './annotation';
4
+ import { RpcUtil, ConverterUtil } from './utils';
5
+ import './index';
6
+
7
+ export default autoBind(bind => {
8
+
9
+ bind(RPC).toDynamicValue(ctx => {
10
+ const id = ctx.currentRequest.target.getCustomTags()?.find(m => m.key === ID_KEY)!.value;
11
+ const path = RpcUtil.toPath(id);
12
+ const proxyProvider = ctx.container.get<ProxyProvider>(ProxyProvider);
13
+ const errorConverters = ConverterUtil.getGlobalErrorConverters(ctx.container);
14
+ const errorConverter = ConverterUtil.getErrorConverters(id, ctx.container);
15
+ if (errorConverter) {
16
+ errorConverters.push(errorConverter);
17
+ }
18
+ return proxyProvider.provide(path, errorConverters);
19
+ });
20
+
21
+ });
@@ -0,0 +1,12 @@
1
+ import { Component } from '@celljs/core';
2
+ import { ClientConfigProcessor } from './processor-protocol';
3
+ import { AxiosRequestConfig } from 'axios';
4
+
5
+ @Component(ClientConfigProcessor)
6
+ export class NoOpClientConfigProcessor implements ClientConfigProcessor {
7
+
8
+ async process(config: AxiosRequestConfig): Promise<void> {
9
+
10
+ }
11
+
12
+ }
@@ -0,0 +1,2 @@
1
+ export * from './processor-protocol';
2
+ export * from './client-config-processor';
@@ -0,0 +1,7 @@
1
+ import { AxiosRequestConfig } from 'axios';
2
+
3
+ export const ClientConfigProcessor = Symbol('ClientConfigProcessor');
4
+
5
+ export interface ClientConfigProcessor {
6
+ process(config: AxiosRequestConfig): Promise<void>;
7
+ }
@@ -0,0 +1,68 @@
1
+ import { Component, Autowired, Value, PostConstruct } from '@celljs/core';
2
+ import { RestOperations } from '@celljs/http';
3
+ import { ProxyCreator } from './proxy-protocol';
4
+ import { EndpointResolver } from '../endpoint';
5
+ import { ConnnectionFactory, JsonRpcProxy } from '../factory';
6
+ import { Channel } from '../channal';
7
+ import { ErrorConverter } from '../converter';
8
+ import { AxiosRequestConfig } from 'axios';
9
+ import { ClientConfigProcessor } from '../processor';
10
+ import { HttpProxyCreator } from './http-proxy-creator';
11
+
12
+ @Component(ProxyCreator)
13
+ export class DelegatingHttpProxyCreator implements ProxyCreator {
14
+
15
+ @Autowired(ConnnectionFactory)
16
+ protected connnectionFactory: ConnnectionFactory<Channel>;
17
+
18
+ @Autowired(EndpointResolver)
19
+ protected endpointResolver: EndpointResolver;
20
+
21
+ @Autowired(RestOperations)
22
+ protected restOperations: RestOperations;
23
+
24
+ @Autowired(ClientConfigProcessor)
25
+ protected clientConfigProcessor: ClientConfigProcessor;
26
+
27
+ @Value('cell.rpc.client.config')
28
+ protected readonly clientConfig: AxiosRequestConfig;
29
+
30
+ @Value('cell.rpc.merge.maxCount')
31
+ protected readonly maxCount: number;
32
+
33
+ @Value('cell.rpc.merge.maxLength')
34
+ protected readonly maxLength: number;
35
+
36
+ @Value('cell.rpc.merge.timerDelay')
37
+ protected readonly timerDelay: number;
38
+
39
+ @Value('cell.rpc.merge.enabled')
40
+ protected readonly enabled: boolean;
41
+
42
+ protected delegate: ProxyCreator;
43
+
44
+ @PostConstruct()
45
+ protected init() {
46
+ this.delegate = new HttpProxyCreator({
47
+ connnectionFactory: this.connnectionFactory,
48
+ endpointResolver: this.endpointResolver,
49
+ restOperations: this.restOperations,
50
+ clientConfigProcessor: this.clientConfigProcessor,
51
+ clientConfig: this.clientConfig,
52
+ merge: {
53
+ maxCount: this.maxCount,
54
+ maxLength: this.maxLength,
55
+ timerDelay: this.timerDelay,
56
+ enabled: this.enabled
57
+ }
58
+ });
59
+ }
60
+
61
+ create<T extends object>(path: string, errorConverters?: ErrorConverter[], target?: object | undefined): JsonRpcProxy<T> {
62
+ return this.delegate.create(path, errorConverters, target);
63
+ }
64
+
65
+ support(path: string): number {
66
+ return this.delegate.support(path);
67
+ }
68
+ }
@@ -0,0 +1,179 @@
1
+ import { HttpHeaders, HttpMethod, MediaType, XML_HTTP_REQUEST, RestOperations, DefaultRestOperationsFactory } from '@celljs/http';
2
+ import { Logger } from 'vscode-jsonrpc';
3
+ import { ProxyCreator, ConnectionOptions, RequestTaskMeta } from './proxy-protocol';
4
+ import { EndpointResolver } from '../endpoint';
5
+ import { ConnnectionFactory, ConnnectionFactoryImpl, JsonRpcProxy, JsonRpcProxyFactory } from '../factory';
6
+ import { Channel, HttpChannel } from '../channal';
7
+ import { ErrorConverter } from '../converter';
8
+ import { ConnectionHandler } from '../handler';
9
+ import { AxiosRequestConfig } from 'axios';
10
+ import { ClientConfigProcessor } from '../processor';
11
+ import { ConsoleLogger } from '../utils';
12
+
13
+ export interface MergeOptions {
14
+ maxCount: number;
15
+ maxLength: number;
16
+ timerDelay: number;
17
+ enabled: boolean;
18
+ }
19
+
20
+ export interface HttpProxyCreatorOptions<T> {
21
+
22
+ connnectionFactory: ConnnectionFactory<Channel>;
23
+ endpointResolver: EndpointResolver;
24
+ restOperations: RestOperations;
25
+ clientConfigProcessor?: ClientConfigProcessor;
26
+ clientConfig: AxiosRequestConfig;
27
+ merge: T;
28
+ }
29
+
30
+ const DEFAULT_OPTIONS: Omit<HttpProxyCreatorOptions<MergeOptions>, 'endpointResolver'> = {
31
+ connnectionFactory: new ConnnectionFactoryImpl(),
32
+ restOperations: new DefaultRestOperationsFactory().create(),
33
+ clientConfig: {},
34
+ merge: {
35
+ maxCount: 100,
36
+ maxLength: 5242880,
37
+ timerDelay: 35,
38
+ enabled: false
39
+ }
40
+
41
+ };
42
+
43
+ export class HttpProxyCreator implements ProxyCreator {
44
+
45
+ protected channelIdSeq = 0;
46
+
47
+ protected requestMap = new Map<string, RequestTaskMeta>();
48
+ protected channelMap = new Map<number, Channel>();
49
+ protected options: HttpProxyCreatorOptions<MergeOptions>;
50
+
51
+ constructor(
52
+ options: Partial<Omit<HttpProxyCreatorOptions<Partial<MergeOptions>>, 'endpointResolver'>> & Pick<HttpProxyCreatorOptions<Partial<MergeOptions>>, 'endpointResolver'>) {
53
+ this.options = { ...DEFAULT_OPTIONS, ...options, merge: { ...DEFAULT_OPTIONS.merge, ...options.merge } };
54
+ }
55
+
56
+ create<T extends object>(path: string, errorConverters?: ErrorConverter[], target?: object | undefined): JsonRpcProxy<T> {
57
+ const factory = new JsonRpcProxyFactory<T>(target, errorConverters);
58
+ this.options.endpointResolver.resolve(path).then(endpoint => this.listen({
59
+ path: endpoint,
60
+ onConnection: c => factory.listen(c)
61
+ }));
62
+ return factory.createProxy();
63
+ }
64
+
65
+ support(path: string): number {
66
+ return 500;
67
+ }
68
+
69
+ listen(handler: ConnectionHandler, options?: ConnectionOptions): void {
70
+ this.openChannel(handler.path, channel => {
71
+ const connection = this.options.connnectionFactory.create(channel, this.createLogger());
72
+ handler.onConnection(connection);
73
+ }, options);
74
+ }
75
+
76
+ openChannel(path: string, handler: (channel: Channel) => void, options?: ConnectionOptions): void {
77
+ this.doOpenChannel(path, handler, options);
78
+ }
79
+
80
+ protected doOpenChannel(path: string, handler: (channel: Channel) => void, options?: ConnectionOptions): void {
81
+ const id = this.channelIdSeq++;
82
+ const channel = this.createChannel(id, path);
83
+ handler(channel);
84
+ }
85
+
86
+ protected createChannel(id: number, path: string): Channel {
87
+ const parts = path.split('/');
88
+ const serviceName = parts.pop();
89
+ const endpoint = parts.join('/');
90
+ const channel = new HttpChannel(id, async content => {
91
+ if (this.options.merge.enabled) {
92
+ if (this.canMerge(endpoint, content)) {
93
+ this.pushContent(endpoint, content);
94
+ } else {
95
+ this.executeTask(endpoint);
96
+ this.pushContent(endpoint, content);
97
+ }
98
+ } else {
99
+ this.pushContent(endpoint, content);
100
+ this.executeTask(endpoint);
101
+ }
102
+
103
+ }, serviceName);
104
+ this.channelMap.set(id, channel);
105
+ return channel;
106
+ }
107
+
108
+ protected executeTask(endpoint: string) {
109
+ const meta = this.requestMap.get(endpoint);
110
+ if (meta) {
111
+ return meta.task();
112
+ }
113
+ }
114
+
115
+ protected canMerge(endpoint: string, content: string) {
116
+ const meta = this.requestMap.get(endpoint);
117
+ if (meta) {
118
+ if (meta.contentLength + content.length > this.options.merge.maxLength ||
119
+ meta.contents.length + 1 > this.options.merge.maxCount) {
120
+ return false;
121
+ }
122
+ }
123
+ return true;
124
+ }
125
+
126
+ protected pushContent(endpoint: string, content: string) {
127
+ let meta = this.requestMap.get(endpoint);
128
+ if (!meta) {
129
+ const task = this.createTask(endpoint);
130
+ meta = {
131
+ id: setTimeout(task, this.options.merge.timerDelay),
132
+ contents: [],
133
+ contentLength: 0,
134
+ task
135
+ };
136
+ this.requestMap.set(endpoint, meta);
137
+ }
138
+ meta.contents.push(content);
139
+ meta.contentLength += content.length;
140
+ return meta;
141
+ }
142
+
143
+ protected createTask(endpoint: string) {
144
+ return async () => {
145
+ const meta = this.requestMap.get(endpoint);
146
+ if (!meta) {
147
+ return;
148
+ }
149
+ clearTimeout(meta.id as any);
150
+ const contents = meta.contents;
151
+ this.requestMap.delete(endpoint);
152
+ const config: AxiosRequestConfig = {
153
+ url: endpoint,
154
+ method: HttpMethod.POST,
155
+ data: contents.length > 1 ? JSON.stringify(contents) : contents[0],
156
+ headers: {
157
+ [HttpHeaders.CONTENT_TYPE]: MediaType.APPLICATION_JSON_UTF8,
158
+ [HttpHeaders.X_REQUESTED_WITH]: XML_HTTP_REQUEST
159
+ },
160
+ ...this.options.clientConfig
161
+ };
162
+ await this.options.clientConfigProcessor?.process(config);
163
+ const { data } = await this.options.restOperations.request(config);
164
+ if (Array.isArray(data)) {
165
+ for (const message of data) {
166
+ const parsed = JSON.parse(message);
167
+ this.channelMap.get(parsed.id)!.handleMessage(parsed);
168
+ }
169
+ } else {
170
+ this.channelMap.get(data.id)!.handleMessage(data);
171
+ }
172
+ };
173
+ }
174
+
175
+ protected createLogger(): Logger {
176
+ return new ConsoleLogger();
177
+ }
178
+
179
+ }
@@ -0,0 +1,5 @@
1
+ export * from './proxy-provider';
2
+ export * from './proxy-protocol';
3
+ export * from './http-proxy-creator';
4
+ export * from './delegating-http-proxy-creator';
5
+
@@ -0,0 +1,26 @@
1
+ import { ErrorConverter } from '../converter';
2
+ import { JsonRpcProxy } from '../factory';
3
+
4
+ export const ProxyProvider = Symbol('ProxyProvider');
5
+
6
+ export interface ProxyProvider {
7
+ provide<T extends object>(path: string, errorConverters?: ErrorConverter[], target?: object): JsonRpcProxy<T>;
8
+ }
9
+
10
+ export const ProxyCreator = Symbol('ProxyCreator');
11
+
12
+ export interface ProxyCreator {
13
+ create<T extends object>(path: string, errorConverters?: ErrorConverter[], target?: object): JsonRpcProxy<T>;
14
+ support(path: string): number
15
+ }
16
+
17
+ export interface ConnectionOptions {
18
+ reconnecting?: boolean;
19
+ }
20
+
21
+ export interface RequestTaskMeta {
22
+ id: number | NodeJS.Timeout;
23
+ contentLength: number;
24
+ contents: string[];
25
+ task: () => Promise<void>;
26
+ }