hypha-rpc 0.21.20 → 0.21.22

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.
@@ -86,7 +86,7 @@
86
86
  <div class='footer quiet pad2 space-top1 center small'>
87
87
  Code coverage generated by
88
88
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
89
- at 2026-02-24T19:02:42.006Z
89
+ at 2026-02-25T19:07:57.766Z
90
90
  </div>
91
91
  <script src="prettify.js"></script>
92
92
  <script>
@@ -2850,7 +2850,7 @@ class HTTPStreamingRPCConnection {
2850
2850
  body: body,
2851
2851
  });
2852
2852
  if (response.ok) {
2853
- console.debug("Token refresh requested successfully");
2853
+ // console.debug("Token refresh requested successfully");
2854
2854
  } else {
2855
2855
  console.warn(`Token refresh request failed: ${response.status}`);
2856
2856
  }
@@ -3848,10 +3848,10 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
3848
3848
  reasonStr.includes("Client disconnected") ||
3849
3849
  reasonStr.includes("RPC connection closed")
3850
3850
  ) {
3851
- console.debug(
3852
- "Ignoring expected disconnection/method error:",
3853
- reason,
3854
- );
3851
+ // console.debug(
3852
+ // "Ignoring expected disconnection/method error:",
3853
+ // reason,
3854
+ // );
3855
3855
  event.preventDefault();
3856
3856
  return;
3857
3857
  }
@@ -3908,6 +3908,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
3908
3908
  this._boundHandleError = console.error;
3909
3909
  this.on("method", this._boundHandleMethod);
3910
3910
  this.on("error", this._boundHandleError);
3911
+ this.on("peer_not_found", this._handlePeerNotFound.bind(this));
3911
3912
 
3912
3913
  (0,_utils_index_js__WEBPACK_IMPORTED_MODULE_0__.assert)(connection.emit_message && connection.on_message);
3913
3914
  (0,_utils_index_js__WEBPACK_IMPORTED_MODULE_0__.assert)(
@@ -3919,7 +3920,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
3919
3920
  this._connection = connection;
3920
3921
  const onConnected = async (connectionInfo) => {
3921
3922
  if (!this._silent && this._connection.manager_id) {
3922
- console.debug("Connection established, reporting services...");
3923
+ // console.debug("Connection established, reporting services...");
3923
3924
  try {
3924
3925
  // Retry getting manager service with exponential backoff
3925
3926
  const manager = await this.get_manager_service({
@@ -3944,9 +3945,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
3944
3945
  `Timeout registering service ${service.id || "unknown"}`,
3945
3946
  );
3946
3947
  registeredCount++;
3947
- console.debug(
3948
- `Successfully registered service: ${service.id || "unknown"}`,
3949
- );
3948
+ // console.debug(
3949
+ // `Successfully registered service: ${service.id || "unknown"}`,
3950
+ // );
3950
3951
  } catch (serviceError) {
3951
3952
  failedServices.push(service.id || "unknown");
3952
3953
  if (
@@ -3998,9 +3999,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
3998
3999
  this._clientDisconnectedSubscription.unsubscribe();
3999
4000
  }
4000
4001
  } catch (e) {
4001
- console.debug(
4002
- `Error unsubscribing old client_disconnected: ${e}`,
4003
- );
4002
+ // console.debug(`Error unsubscribing old client_disconnected: ${e}`);
4004
4003
  }
4005
4004
  this._clientDisconnectedSubscription = null;
4006
4005
  }
@@ -4016,7 +4015,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4016
4015
  this._boundHandleClientDisconnected = null;
4017
4016
  }
4018
4017
 
4019
- console.debug("Subscribing to client_disconnected events");
4018
+ // console.debug("Subscribing to client_disconnected events");
4020
4019
 
4021
4020
  // Store handler at instance level so it can be removed later
4022
4021
  this._boundHandleClientDisconnected = async (event) => {
@@ -4026,14 +4025,14 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4026
4025
  if (clientId && workspace) {
4027
4026
  // Construct the full client path with workspace prefix
4028
4027
  const fullClientId = `${workspace}/${clientId}`;
4029
- console.debug(
4030
- `Client ${fullClientId} disconnected, cleaning up sessions`,
4031
- );
4028
+ // console.debug(
4029
+ // `Client ${fullClientId} disconnected, cleaning up sessions`,
4030
+ // );
4032
4031
  await this._handleClientDisconnected(fullClientId);
4033
4032
  } else if (clientId) {
4034
- console.debug(
4035
- `Client ${clientId} disconnected, cleaning up sessions`,
4036
- );
4033
+ // console.debug(
4034
+ // `Client ${clientId} disconnected, cleaning up sessions`,
4035
+ // );
4037
4036
  await this._handleClientDisconnected(clientId);
4038
4037
  }
4039
4038
  };
@@ -4051,19 +4050,19 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4051
4050
  this._boundHandleClientDisconnected,
4052
4051
  );
4053
4052
 
4054
- console.debug(
4055
- "Successfully subscribed to client_disconnected events",
4056
- );
4053
+ // console.debug(
4054
+ // "Successfully subscribed to client_disconnected events",
4055
+ // );
4057
4056
  } else {
4058
- console.debug(
4059
- "Manager does not support subscribe method, skipping client_disconnected handling",
4060
- );
4057
+ // console.debug(
4058
+ // "Manager does not support subscribe method, skipping client_disconnected handling",
4059
+ // );
4061
4060
  this._clientDisconnectedSubscription = null;
4062
4061
  }
4063
4062
  } catch (subscribeError) {
4064
- console.debug(
4065
- `Failed to subscribe to client_disconnected events: ${subscribeError}`,
4066
- );
4063
+ // console.debug(
4064
+ // `Failed to subscribe to client_disconnected events: ${subscribeError}`,
4065
+ // );
4067
4066
  this._clientDisconnectedSubscription = null;
4068
4067
  }
4069
4068
  } catch (managerError) {
@@ -4369,7 +4368,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4369
4368
  this._clientDisconnectedSubscription.unsubscribe();
4370
4369
  }
4371
4370
  } catch (e) {
4372
- console.debug(`Error unsubscribing client_disconnected: ${e}`);
4371
+ // console.debug(`Error unsubscribing client_disconnected: ${e}`);
4373
4372
  }
4374
4373
  this._clientDisconnectedSubscription = null;
4375
4374
  }
@@ -4392,13 +4391,13 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4392
4391
  try {
4393
4392
  task.cancel();
4394
4393
  } catch (e) {
4395
- console.debug(`Error canceling background task: ${e}`);
4394
+ // console.debug(`Error canceling background task: ${e}`);
4396
4395
  }
4397
4396
  }
4398
4397
  }
4399
4398
  this._background_tasks.clear();
4400
4399
  } catch (e) {
4401
- console.debug(`Error cleaning up background tasks: ${e}`);
4400
+ // console.debug(`Error cleaning up background tasks: ${e}`);
4402
4401
  }
4403
4402
 
4404
4403
  // Clear session sweep interval
@@ -4411,11 +4410,11 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4411
4410
  try {
4412
4411
  this._connection = null;
4413
4412
  this._emit_message = function () {
4414
- console.debug("RPC connection closed, ignoring message");
4413
+ // console.debug("RPC connection closed, ignoring message");
4415
4414
  return Promise.reject(new Error("Connection is closed"));
4416
4415
  };
4417
4416
  } catch (e) {
4418
- console.debug(`Error during connection cleanup: ${e}`);
4417
+ // console.debug(`Error during connection cleanup: ${e}`);
4419
4418
  }
4420
4419
 
4421
4420
  this._fire("disconnected");
@@ -4423,15 +4422,15 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4423
4422
 
4424
4423
  async _handleClientDisconnected(clientId) {
4425
4424
  try {
4426
- console.debug(`Handling disconnection for client: ${clientId}`);
4425
+ // console.debug(`Handling disconnection for client: ${clientId}`);
4427
4426
 
4428
4427
  // Clean up all sessions for the disconnected client
4429
4428
  const sessionsCleaned = this._cleanupSessionsForClient(clientId);
4430
4429
 
4431
4430
  if (sessionsCleaned > 0) {
4432
- console.debug(
4433
- `Cleaned up ${sessionsCleaned} sessions for disconnected client: ${clientId}`,
4434
- );
4431
+ // console.debug(
4432
+ // `Cleaned up ${sessionsCleaned} sessions for disconnected client: ${clientId}`,
4433
+ // );
4435
4434
  }
4436
4435
 
4437
4436
  // Fire an event to notify about the client disconnection
@@ -4446,6 +4445,35 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4446
4445
  }
4447
4446
  }
4448
4447
 
4448
+ _handlePeerNotFound(data) {
4449
+ /**
4450
+ * Handle server notification that target peer is not connected.
4451
+ *
4452
+ * When the server detects that an RPC message targets a disconnected
4453
+ * client, it sends back a 'peer_not_found' message instead of silently
4454
+ * dropping it. This allows pending calls to fail immediately.
4455
+ */
4456
+ const sessionId = data.session;
4457
+ const peerId = data.peer_id || data.from || "unknown";
4458
+ const errorMsg = data.error || `Peer ${peerId} is not connected`;
4459
+ // console.debug(`Peer not found: ${peerId} (session=${sessionId})`);
4460
+
4461
+ // Reject the specific pending call identified by sessionId
4462
+ if (sessionId) {
4463
+ const session = this._object_store[sessionId];
4464
+ if (session && typeof session === "object") {
4465
+ this._cleanupSessionEntry(session, errorMsg);
4466
+ delete this._object_store[sessionId];
4467
+ this._removeFromTargetIdIndex(sessionId);
4468
+ }
4469
+ }
4470
+
4471
+ // Also clean up all other sessions targeting this peer
4472
+ if (peerId) {
4473
+ this._cleanupSessionsForClient(peerId);
4474
+ }
4475
+ }
4476
+
4449
4477
  _removeFromTargetIdIndex(sessionId) {
4450
4478
  /**
4451
4479
  * Remove a session from the target_id index.
@@ -4480,7 +4508,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4480
4508
  try {
4481
4509
  session.reject(new Error(rejectReason));
4482
4510
  } catch (e) {
4483
- console.debug(`Error rejecting session: ${e}`);
4511
+ // console.debug(`Error rejecting session: ${e}`);
4484
4512
  }
4485
4513
  }
4486
4514
  if (session.heartbeat_task) {
@@ -4519,7 +4547,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4519
4547
  this._cleanupSessionEntry(session, reason);
4520
4548
  delete this._object_store[sessionKey];
4521
4549
  sessionsCleaned++;
4522
- console.debug(`Cleaned up session: ${sessionKey}`);
4550
+ // console.debug(`Cleaned up session: ${sessionKey}`);
4523
4551
  }
4524
4552
 
4525
4553
  delete this._targetIdIndex[clientId];
@@ -4555,7 +4583,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4555
4583
 
4556
4584
  _cleanupOnDisconnect() {
4557
4585
  try {
4558
- console.debug("Cleaning up all sessions due to local RPC disconnection");
4586
+ // console.debug("Cleaning up all sessions due to local RPC disconnection");
4559
4587
 
4560
4588
  const keysToDelete = [];
4561
4589
  for (const key of Object.keys(this._object_store)) {
@@ -4585,7 +4613,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4585
4613
  try {
4586
4614
  await connection.disconnect();
4587
4615
  } catch (e) {
4588
- console.debug(`Error disconnecting underlying connection: ${e}`);
4616
+ // console.debug(`Error disconnecting underlying connection: ${e}`);
4589
4617
  }
4590
4618
  }
4591
4619
  }
@@ -5079,14 +5107,14 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5079
5107
  * Clean session management - all logic in one place.
5080
5108
  */
5081
5109
  if (!session_id) {
5082
- console.debug("Cannot cleanup session: session_id is empty");
5110
+ // console.debug("Cannot cleanup session: session_id is empty");
5083
5111
  return;
5084
5112
  }
5085
5113
 
5086
5114
  try {
5087
5115
  const store = this._get_session_store(session_id, false);
5088
5116
  if (!store) {
5089
- console.debug(`Session ${session_id} not found for cleanup`);
5117
+ // console.debug(`Session ${session_id} not found for cleanup`);
5090
5118
  return;
5091
5119
  }
5092
5120
 
@@ -5104,9 +5132,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5104
5132
  promise_manager.settle();
5105
5133
  }
5106
5134
  should_cleanup = true;
5107
- console.debug(
5108
- `Promise session ${session_id} settled and marked for cleanup`,
5109
- );
5135
+ // console.debug(
5136
+ // `Promise session ${session_id} settled and marked for cleanup`,
5137
+ // );
5110
5138
  }
5111
5139
  } catch (e) {
5112
5140
  console.warn(
@@ -5124,9 +5152,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5124
5152
  Object.keys(store._callbacks).includes(callback_name)
5125
5153
  ) {
5126
5154
  should_cleanup = true;
5127
- console.debug(
5128
- `Regular session ${session_id} marked for cleanup after ${callback_name}`,
5129
- );
5155
+ // console.debug(
5156
+ // `Regular session ${session_id} marked for cleanup after ${callback_name}`,
5157
+ // );
5130
5158
  }
5131
5159
  }
5132
5160
 
@@ -5148,7 +5176,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5148
5176
 
5149
5177
  const store = this._get_session_store(session_id, false);
5150
5178
  if (!store) {
5151
- console.debug(`Session ${session_id} already cleaned up`);
5179
+ // console.debug(`Session ${session_id} already cleaned up`);
5152
5180
  return;
5153
5181
  }
5154
5182
 
@@ -5190,9 +5218,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5190
5218
  for (let i = 0; i < levels.length - 1; i++) {
5191
5219
  const level = levels[i];
5192
5220
  if (!current_store[level]) {
5193
- console.debug(
5194
- `Session path ${session_id} not found at level ${level}`,
5195
- );
5221
+ // console.debug(
5222
+ // `Session path ${session_id} not found at level ${level}`,
5223
+ // );
5196
5224
  return;
5197
5225
  }
5198
5226
  current_store = current_store[level];
@@ -5202,7 +5230,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5202
5230
  const final_key = levels[levels.length - 1];
5203
5231
  if (current_store[final_key]) {
5204
5232
  delete current_store[final_key];
5205
- console.debug(`Cleaned up session ${session_id}`);
5233
+ // console.debug(`Cleaned up session ${session_id}`);
5206
5234
 
5207
5235
  // Clean up empty parent containers
5208
5236
  this._cleanup_empty_containers(levels.slice(0, -1));
@@ -5240,9 +5268,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5240
5268
  Object.keys(container).length === 0
5241
5269
  ) {
5242
5270
  delete current_store[container_key];
5243
- console.debug(
5244
- `Cleaned up empty container at depth ${depth}: ${path_levels.slice(0, depth + 1).join(".")}`,
5245
- );
5271
+ // console.debug(
5272
+ // `Cleaned up empty container at depth ${depth}: ${path_levels.slice(0, depth + 1).join(".")}`,
5273
+ // );
5246
5274
  } else {
5247
5275
  // Container is not empty, stop cleanup
5248
5276
  break;
@@ -5317,7 +5345,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5317
5345
  * Force cleanup all sessions (for testing purposes).
5318
5346
  */
5319
5347
  if (!this._object_store) {
5320
- console.debug("Force cleaning up 0 sessions");
5348
+ // console.debug("Force cleaning up 0 sessions");
5321
5349
  return;
5322
5350
  }
5323
5351
 
@@ -5347,7 +5375,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5347
5375
  // Clear the target_id index since all sessions are removed
5348
5376
  this._targetIdIndex = {};
5349
5377
 
5350
- console.debug(`Force cleaning up ${cleaned_count} sessions`);
5378
+ // console.debug(`Force cleaning up ${cleaned_count} sessions`);
5351
5379
  }
5352
5380
 
5353
5381
  _sweepStaleSessions() {
@@ -5383,7 +5411,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5383
5411
  }
5384
5412
  }
5385
5413
  if (swept > 0) {
5386
- console.debug(`Swept ${swept} stale session(s)`);
5414
+ // console.debug(`Swept ${swept} stale session(s)`);
5387
5415
  }
5388
5416
  }
5389
5417
 
@@ -5405,7 +5433,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5405
5433
  },
5406
5434
  settle: () => {
5407
5435
  // Promise is settled (resolved or rejected)
5408
- console.debug("Promise settled");
5436
+ // console.debug("Promise settled");
5409
5437
  },
5410
5438
  };
5411
5439
  }
@@ -5676,9 +5704,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5676
5704
  // Clean up target_id index before deleting the session
5677
5705
  self._removeFromTargetIdIndex(local_session_id);
5678
5706
  delete self._object_store[local_session_id];
5679
- console.debug(
5680
- `Cleaned up session ${local_session_id} after timeout`,
5681
- );
5707
+ // console.debug(
5708
+ // `Cleaned up session ${local_session_id} after timeout`,
5709
+ // );
5682
5710
  }
5683
5711
  };
5684
5712
 
@@ -5961,9 +5989,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5961
5989
  } catch (e) {
5962
5990
  // Clean promise method detection - TYPE-BASED, not string-based
5963
5991
  if (this._is_promise_method_call(data["method"])) {
5964
- console.debug(
5965
- `Promise method ${data["method"]} not available (detected by session type), ignoring: ${method_name}`,
5966
- );
5992
+ // console.debug(
5993
+ // `Promise method ${data["method"]} not available (detected by session type), ignoring: ${method_name}`,
5994
+ // );
5967
5995
  return;
5968
5996
  }
5969
5997
 
@@ -5973,18 +6001,18 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5973
6001
  const session_id = method_parts[0];
5974
6002
  // Check if the session exists but the specific method doesn't
5975
6003
  if (session_id in this._object_store) {
5976
- console.debug(
5977
- `Session ${session_id} exists but method ${data["method"]} not found, likely expired callback: ${method_name}`,
5978
- );
6004
+ // console.debug(
6005
+ // `Session ${session_id} exists but method ${data["method"]} not found, likely expired callback: ${method_name}`,
6006
+ // );
5979
6007
  // For expired callbacks, don't throw an exception, just log and return
5980
6008
  if (typeof reject === "function") {
5981
6009
  reject(new Error(`Method expired or not found: ${method_name}`));
5982
6010
  }
5983
6011
  return;
5984
6012
  } else {
5985
- console.debug(
5986
- `Session ${session_id} not found for method ${data["method"]}, likely cleaned up: ${method_name}`,
5987
- );
6013
+ // console.debug(
6014
+ // `Session ${session_id} not found for method ${data["method"]}, likely cleaned up: ${method_name}`,
6015
+ // );
5988
6016
  // For cleaned up sessions, just log and return without throwing
5989
6017
  if (typeof reject === "function") {
5990
6018
  reject(new Error(`Session not found: ${method_name}`));
@@ -5993,9 +6021,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5993
6021
  }
5994
6022
  }
5995
6023
 
5996
- console.debug(
5997
- `Failed to find method ${method_name} at ${this._client_id}`,
5998
- );
6024
+ // console.debug(
6025
+ // `Failed to find method ${method_name} at ${this._client_id}`,
6026
+ // );
5999
6027
  const error = new Error(
6000
6028
  `Method not found: ${method_name} at ${this._client_id}`,
6001
6029
  );
@@ -9938,6 +9966,7 @@ __webpack_require__.r(__webpack_exports__);
9938
9966
  /* harmony export */ HTTPStreamingRPCConnection: () => (/* reexport safe */ _http_client_js__WEBPACK_IMPORTED_MODULE_5__.HTTPStreamingRPCConnection),
9939
9967
  /* harmony export */ LocalWebSocket: () => (/* binding */ LocalWebSocket),
9940
9968
  /* harmony export */ RPC: () => (/* reexport safe */ _rpc_js__WEBPACK_IMPORTED_MODULE_0__.RPC),
9969
+ /* harmony export */ WebsocketRPCConnection: () => (/* binding */ WebsocketRPCConnection),
9941
9970
  /* harmony export */ connectToServer: () => (/* binding */ connectToServer),
9942
9971
  /* harmony export */ connectToServerHTTP: () => (/* reexport safe */ _http_client_js__WEBPACK_IMPORTED_MODULE_5__.connectToServerHTTP),
9943
9972
  /* harmony export */ decryptPayload: () => (/* reexport safe */ _crypto_js__WEBPACK_IMPORTED_MODULE_1__.decryptPayload),
@@ -10019,6 +10048,7 @@ class WebsocketRPCConnection {
10019
10048
  this._reconnect_timeouts = new Set(); // Track reconnection timeouts
10020
10049
  this._additional_headers = additional_headers;
10021
10050
  this._reconnecting = false; // Mutex to prevent overlapping reconnection attempts
10051
+ this._closedDuringReconnect = false; // Flag for close events during reconnection
10022
10052
  this._disconnectedNotified = false;
10023
10053
  }
10024
10054
 
@@ -10261,7 +10291,9 @@ class WebsocketRPCConnection {
10261
10291
  this._websocket.onclose = this._handle_close.bind(this);
10262
10292
 
10263
10293
  if (this._handle_connected) {
10264
- this._handle_connected(this.connection_info);
10294
+ // Await async callbacks so errors (e.g. service re-registration
10295
+ // failures) propagate instead of becoming unhandled rejections.
10296
+ await this._handle_connected(this.connection_info);
10265
10297
  }
10266
10298
  return this.connection_info;
10267
10299
  } catch (error) {
@@ -10327,9 +10359,10 @@ class WebsocketRPCConnection {
10327
10359
  // Notify the RPC layer immediately so it can reject pending calls
10328
10360
  this._notifyDisconnected(event.reason);
10329
10361
 
10330
- // Prevent overlapping reconnection attempts
10362
+ // If a reconnection is already in progress, signal it so the
10363
+ // reconnect loop can detect that the newly-opened socket died and retry.
10331
10364
  if (this._reconnecting) {
10332
- console.debug("Reconnection already in progress, skipping");
10365
+ this._closedDuringReconnect = true;
10333
10366
  return;
10334
10367
  }
10335
10368
  this._reconnecting = true;
@@ -10351,6 +10384,10 @@ class WebsocketRPCConnection {
10351
10384
  console.warn(
10352
10385
  `Reconnecting to ${this._server_url.split("?")[0]} (attempt #${retry})`,
10353
10386
  );
10387
+ // Reset the flag before each attempt so we can detect new close
10388
+ // events that arrive while open() and the settle period run.
10389
+ this._closedDuringReconnect = false;
10390
+
10354
10391
  // Open the connection, this will trigger the on_connected callback
10355
10392
  await this.open();
10356
10393
 
@@ -10359,6 +10396,22 @@ class WebsocketRPCConnection {
10359
10396
  // which includes re-registering all services to the server
10360
10397
  await new Promise((resolve) => setTimeout(resolve, 500));
10361
10398
 
10399
+ // Check if the WebSocket died during the settle period.
10400
+ // This handles the race where _handle_close fires while
10401
+ // _reconnecting is true and sets _closedDuringReconnect.
10402
+ if (
10403
+ this._closedDuringReconnect ||
10404
+ !this._websocket ||
10405
+ this._websocket.readyState !== WebSocket.OPEN
10406
+ ) {
10407
+ console.warn(
10408
+ "WebSocket closed during reconnection settle period, retrying...",
10409
+ );
10410
+ this._closedDuringReconnect = false;
10411
+ // Fall through to the retry logic below
10412
+ throw new Error("Connection lost during reconnection settle");
10413
+ }
10414
+
10362
10415
  console.warn(
10363
10416
  `Successfully reconnected to server ${this._server_url} (services re-registered)`,
10364
10417
  );
@@ -10402,9 +10455,7 @@ class WebsocketRPCConnection {
10402
10455
  const jitter = (Math.random() * 2 - 1) * maxJitter * delay;
10403
10456
  const finalDelay = Math.max(100, delay + jitter);
10404
10457
 
10405
- console.debug(
10406
- `Waiting ${(finalDelay / 1000).toFixed(2)}s before next reconnection attempt`,
10407
- );
10458
+ // console.debug(`Waiting ${(finalDelay / 1000).toFixed(2)}s before next reconnection attempt`);
10408
10459
 
10409
10460
  // Track the reconnection timeout to prevent leaks
10410
10461
  const timeoutId = setTimeout(async () => {
@@ -10469,6 +10520,7 @@ class WebsocketRPCConnection {
10469
10520
  disconnect(reason) {
10470
10521
  this._closed = true;
10471
10522
  this._reconnecting = false;
10523
+ this._closedDuringReconnect = false;
10472
10524
  // Ensure websocket is closed if it exists and is not already closed or closing
10473
10525
  if (
10474
10526
  this._websocket &&
@@ -11143,6 +11195,8 @@ class LocalWebSocket {
11143
11195
  // hypha-core's deno build does: import { hyphaWebsocketClient } from 'hypha-rpc'
11144
11196
  // The UMD build wraps everything under this name via webpack's `library` option,
11145
11197
  // but the ESM build exports flat, so we need this explicit re-export.
11198
+
11199
+
11146
11200
  const hyphaWebsocketClient = {
11147
11201
  RPC: _rpc_js__WEBPACK_IMPORTED_MODULE_0__.RPC,
11148
11202
  API_VERSION: _rpc_js__WEBPACK_IMPORTED_MODULE_0__.API_VERSION,