hypha-rpc 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  import { hyphaWebsocketClient } from "hypha-rpc";
9
9
 
10
10
  hyphaWebsocketClient.connectToServer({
11
- server_url: 'https://hypha.aicell.io',
11
+ server_url: 'https://ai.imjoy.io',
12
12
  }).then(async (api)=>{
13
13
  await api.register_service(
14
14
  {
@@ -223,6 +223,7 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
223
223
  },
224
224
  });
225
225
  this.on("method", this._handle_method.bind(this));
226
+ this.on("error", console.error);
226
227
 
227
228
  (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(connection.emit_message && connection.on_message);
228
229
  this._emit_message = connection.emit_message.bind(connection);
@@ -365,8 +366,11 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
365
366
  }
366
367
 
367
368
  _on_message(message) {
368
- try {
369
- (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(message instanceof ArrayBuffer);
369
+ if(typeof message === "string"){
370
+ const main = JSON.parse(message);
371
+ this._fire(main["type"], main);
372
+ }
373
+ else if (message instanceof ArrayBuffer) {
370
374
  let unpacker = (0,_msgpack_msgpack__WEBPACK_IMPORTED_MODULE_1__.decodeMulti)(message);
371
375
  const { done, value } = unpacker.next();
372
376
  const main = value;
@@ -378,8 +382,9 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
378
382
  Object.assign(main, extra.value);
379
383
  }
380
384
  this._fire(main["type"], main);
381
- } catch (error) {
382
- console.error("Failed to process message", error);
385
+ }
386
+ else{
387
+ throw new Error("Invalid message format");
383
388
  }
384
389
  }
385
390
 
@@ -418,7 +423,7 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
418
423
  throw new Error("Service not found: " + service_id);
419
424
  }
420
425
 
421
- service.config["workspace"] = ws;
426
+ service.config["workspace"] = context["ws"];
422
427
  // allow access for the same workspace
423
428
  if (service.config.visibility == "public") {
424
429
  return service;
@@ -1966,6 +1971,11 @@ class WebRTCConnection {
1966
1971
  this._data_channel = channel;
1967
1972
  this._handle_message = null;
1968
1973
  this._reconnection_token = null;
1974
+ this._disconnect_handler = null;
1975
+ this._handle_connect = () => {};
1976
+ this._data_channel.onopen = async ()=>{
1977
+ this._handle_connect && this._handle_connect();
1978
+ }
1969
1979
  this._data_channel.onmessage = async (event) => {
1970
1980
  let data = event.data;
1971
1981
  if (data instanceof Blob) {
@@ -1975,11 +1985,21 @@ class WebRTCConnection {
1975
1985
  };
1976
1986
  const self = this;
1977
1987
  this._data_channel.onclose = function () {
1988
+ if(this._disconnect_handler)
1989
+ this._disconnect_handler("closed");
1978
1990
  console.log("websocket closed");
1979
1991
  self._data_channel = null;
1980
1992
  };
1981
1993
  }
1982
1994
 
1995
+ on_disconnected(handler) {
1996
+ this._disconnect_handler = handler;
1997
+ }
1998
+
1999
+ on_connect(handler) {
2000
+ this._handle_connect = handler;
2001
+ }
2002
+
1983
2003
  on_message(handler) {
1984
2004
  (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.assert)(handler, "handler is required");
1985
2005
  this._handle_message = handler;
@@ -2039,7 +2059,7 @@ async function _createOffer(params, server, config, onInit, context) {
2039
2059
  pc.addEventListener("datachannel", async (event) => {
2040
2060
  const channel = event.channel;
2041
2061
  let ctx = null;
2042
- if (context && context.user) ctx = { user: context.user };
2062
+ if (context && context.user) ctx = { user: context.user, ws: context.ws};
2043
2063
  const rpc = await _setupRPC({
2044
2064
  channel: channel,
2045
2065
  client_id: channel.label,
@@ -2085,7 +2105,13 @@ async function getRTCService(server, service_id, config) {
2085
2105
  () => {
2086
2106
  if (pc.connectionState === "failed") {
2087
2107
  pc.close();
2088
- reject(new Error("Connection failed"));
2108
+ reject(new Error("WebRTC Connection failed"));
2109
+ }
2110
+ else if (pc.connectionState === "closed") {
2111
+ reject(new Error("WebRTC Connection closed"));
2112
+ }
2113
+ else{
2114
+ console.log("WebRTC Connection state: ", pc.connectionState);
2089
2115
  }
2090
2116
  },
2091
2117
  false,
@@ -2152,7 +2178,7 @@ async function registerRTCService(server, service_id, config) {
2152
2178
  };
2153
2179
  const onInit = config.on_init;
2154
2180
  delete config.on_init;
2155
- await server.registerService({
2181
+ return await server.registerService({
2156
2182
  id: service_id,
2157
2183
  config,
2158
2184
  offer: (params, context) =>
@@ -4142,7 +4168,7 @@ __webpack_require__.r(__webpack_exports__);
4142
4168
 
4143
4169
 
4144
4170
 
4145
- const MAX_RETRY = 10000;
4171
+ const MAX_RETRY = 1000000;
4146
4172
 
4147
4173
  class WebsocketRPCConnection {
4148
4174
  constructor(
@@ -4165,9 +4191,10 @@ class WebsocketRPCConnection {
4165
4191
  this._disconnect_handler = null; // Disconnection event handler
4166
4192
  this._timeout = timeout * 1000; // Convert seconds to milliseconds
4167
4193
  this._WebSocketClass = WebSocketClass || WebSocket; // Allow overriding the WebSocket class
4168
- this._closing = false;
4194
+ this._closed = false;
4169
4195
  this._legacy_auth = null;
4170
4196
  this.connection_info = null;
4197
+ this._enable_reconnect = false;
4171
4198
  }
4172
4199
 
4173
4200
  on_message(handler) {
@@ -4204,11 +4231,12 @@ class WebsocketRPCConnection {
4204
4231
  console.info(
4205
4232
  "Received 1003 error, attempting connection with query parameters.",
4206
4233
  );
4234
+ this._legacy_auth = true;
4207
4235
  this._attempt_connection_with_query_params(server_url)
4208
4236
  .then(resolve)
4209
4237
  .catch(reject);
4210
4238
  } else if (this._disconnect_handler) {
4211
- this._disconnect_handler(this, event.reason);
4239
+ this._disconnect_handler(event.reason);
4212
4240
  }
4213
4241
  };
4214
4242
  });
@@ -4237,14 +4265,49 @@ class WebsocketRPCConnection {
4237
4265
  // Construct the full URL by appending the query string if it exists
4238
4266
  const full_url = server_url + queryString;
4239
4267
 
4240
- this._legacy_auth = true; // Assuming this flag is needed for some other logic
4241
4268
  return await this._attempt_connection(full_url, false);
4242
4269
  }
4243
4270
 
4271
+ _establish_connection() {
4272
+ return new Promise((resolve, reject) => {
4273
+ this._websocket.onmessage = (event) => {
4274
+ const data = event.data;
4275
+ const first_message = JSON.parse(data);
4276
+ if (first_message.type == "connection_info") {
4277
+ console.log(
4278
+ "Successfully connected: " + JSON.stringify(first_message),
4279
+ );
4280
+ this.connection_info = first_message;
4281
+ if (this.connection_info.reconnection_token) {
4282
+ this._reconnection_token =
4283
+ this.connection_info.reconnection_token;
4284
+ }
4285
+ resolve(this.connection_info);
4286
+ }
4287
+ else if (first_message.type == "error") {
4288
+ const error = first_message.error || "Unknown error";
4289
+ console.error("Failed to connect, " + error);
4290
+ this.connection_info = null;
4291
+ reject(new Error(error));
4292
+ return;
4293
+ } else {
4294
+ console.error(
4295
+ "Unexpected message received from the server:",
4296
+ data,
4297
+ );
4298
+ reject(new Error("Unexpected message received from the server"));
4299
+ return;
4300
+ }
4301
+
4302
+ };
4303
+ })
4304
+ }
4305
+
4244
4306
  async open() {
4245
- if (this._closing || this._websocket) {
4246
- return; // Avoid opening a new connection if closing or already open
4307
+ if (this._closed){
4308
+ throw new Error("Connection is closing");
4247
4309
  }
4310
+ console.log("Creating a new websocket connection to", this._server_url.split("?")[0]);
4248
4311
  try {
4249
4312
  this._websocket = await this._attempt_connection(this._server_url);
4250
4313
  if (!this._legacy_auth) {
@@ -4258,48 +4321,74 @@ class WebsocketRPCConnection {
4258
4321
  this._websocket.send(authInfo);
4259
4322
  // Wait for the first message from the server
4260
4323
  await (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.waitFor)(
4261
- new Promise((resolve, reject) => {
4262
- this._websocket.onmessage = (event) => {
4263
- const data = event.data;
4264
- const first_message = JSON.parse(data);
4265
- if (!first_message.success) {
4266
- const error = first_message.error || "Unknown error";
4267
- console.error("Failed to connect, " + error);
4268
- this.connection_info = null;
4269
- reject(new Error(error));
4270
- } else if (first_message) {
4271
- console.log(
4272
- "Successfully connected: " + JSON.stringify(first_message),
4273
- );
4274
- this.connection_info = first_message;
4275
- if (this.connection_info.reconnection_token) {
4276
- this._reconnection_token =
4277
- this.connection_info.reconnection_token;
4278
- }
4279
- }
4280
- resolve();
4281
- };
4282
- }),
4324
+ this._establish_connection(),
4283
4325
  this._timeout / 1000.0,
4284
4326
  "Failed to receive the first message from the server",
4285
4327
  );
4286
4328
  }
4287
-
4329
+ else{
4330
+ throw new Error("Legacy authentication is not supported");
4331
+ }
4332
+ // Listen to messages from the server
4333
+ this._enable_reconnect = true;
4334
+ this._closed = false;
4288
4335
  this._websocket.onmessage = (event) => {
4289
4336
  this._handle_message(event.data);
4290
4337
  };
4291
4338
 
4339
+ this._websocket.onclose = this._handle_close.bind(this);
4340
+
4292
4341
  if (this._handle_connect) {
4293
- await this._handle_connect(this);
4342
+ this._handle_connect(this);
4294
4343
  }
4344
+ return this.connection_info;
4295
4345
  } catch (error) {
4296
- console.error("Failed to connect to", this._server_url, error);
4346
+ console.error("Failed to connect to", this._server_url.split("?")[0], error);
4347
+ throw error;
4348
+ }
4349
+ }
4350
+
4351
+ _handle_close(event) {
4352
+ if (!this._closed && this._websocket && this._websocket.readyState === WebSocket.CLOSED) {
4353
+ if ([1000].includes(event.code)) {
4354
+ console.info("Websocket connection closed (code: %s): %s", event.code, event.reason);
4355
+ if (this._disconnect_handler) {
4356
+ this._disconnect_handler(event.reason);
4357
+ }
4358
+ this._closed = true;
4359
+ } else if (this._enable_reconnect) {
4360
+ console.warn("Websocket connection closed unexpectedly (code: %s): %s", event.code, event.reason);
4361
+ let retry = 0;
4362
+ const reconnect = async () => {
4363
+ try {
4364
+ console.info("Reconnecting to", this._server_url.split("?")[0]);
4365
+ await this.open();
4366
+ } catch (e) {
4367
+ console.warn("Failed to reconnect:", e);
4368
+ await new Promise((resolve) => setTimeout(resolve, 1000));
4369
+ if(this._websocket && this._websocket.readyState === WebSocket.CONNECTED){
4370
+ return;
4371
+ }
4372
+ retry += 1;
4373
+ if (retry < MAX_RETRY) {
4374
+ await reconnect();
4375
+ } else {
4376
+ console.error("Failed to reconnect after", MAX_RETRY, "attempts");
4377
+ }
4378
+ }
4379
+ };
4380
+ reconnect();
4381
+ }
4382
+ } else {
4383
+ if (this._disconnect_handler) {
4384
+ this._disconnect_handler(event.reason);
4385
+ }
4297
4386
  }
4298
4387
  }
4299
4388
 
4300
4389
  async emit_message(data) {
4301
- if (this._closing) {
4302
- throw new Error("Connection is closing");
4390
+ if (this._closed) {
4391
+ throw new Error("Connection is closed");
4303
4392
  }
4304
4393
  if (!this._websocket || this._websocket.readyState !== WebSocket.OPEN) {
4305
4394
  await this.open();
@@ -4313,11 +4402,12 @@ class WebsocketRPCConnection {
4313
4402
  }
4314
4403
 
4315
4404
  disconnect(reason) {
4316
- this._closing = true;
4405
+ this._closed = true;
4317
4406
  if (this._websocket && this._websocket.readyState === WebSocket.OPEN) {
4318
4407
  this._websocket.close(1000, reason);
4319
- console.info(`WebSocket connection disconnected (${reason})`);
4408
+
4320
4409
  }
4410
+ console.info(`WebSocket connection disconnected (${reason})`);
4321
4411
  }
4322
4412
  }
4323
4413
 
@@ -4380,18 +4470,18 @@ async function connectToServer(config) {
4380
4470
  config.method_timeout || 60,
4381
4471
  config.WebSocketClass,
4382
4472
  );
4383
- await connection.open();
4384
- (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.assert)(connection.connection_info, "Failed to connect to the server");
4473
+ const connection_info = await connection.open();
4474
+ (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.assert)(connection_info, "Failed to connect to the server, no connection info obtained. This issue is most likely due to an outdated Hypha server version. Please use `imjoy-rpc` for compatibility, or upgrade the Hypha server to the latest version.");
4385
4475
  if (
4386
4476
  config.workspace &&
4387
- connection.connection_info.workspace !== config.workspace
4477
+ connection_info.workspace !== config.workspace
4388
4478
  ) {
4389
4479
  throw new Error(
4390
- `Connected to the wrong workspace: ${connection.connection_info.workspace}, expected: ${config.workspace}`,
4480
+ `Connected to the wrong workspace: ${connection_info.workspace}, expected: ${config.workspace}`,
4391
4481
  );
4392
4482
  }
4393
- const workspace = connection.connection_info.workspace;
4394
- const manager_id = connection.connection_info.manager_id;
4483
+ const workspace = connection_info.workspace;
4484
+ const manager_id = connection_info.manager_id;
4395
4485
  const rpc = new _rpc_js__WEBPACK_IMPORTED_MODULE_0__.RPC(connection, {
4396
4486
  client_id: clientId,
4397
4487
  workspace,
@@ -4419,8 +4509,8 @@ async function connectToServer(config) {
4419
4509
  await rpc.disconnect();
4420
4510
  await connection.disconnect();
4421
4511
  }
4422
- if (connection.connection_info) {
4423
- wm.config = Object.assign(wm.config, connection.connection_info);
4512
+ if (connection_info) {
4513
+ wm.config = Object.assign(wm.config, connection_info);
4424
4514
  }
4425
4515
  wm.export = _export;
4426
4516
  wm.getPlugin = getPlugin;