hypha-rpc 0.21.19 → 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-24T16:05:09.783Z
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 (
@@ -3987,23 +3988,51 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
3987
3988
  manager.subscribe &&
3988
3989
  typeof manager.subscribe === "function"
3989
3990
  ) {
3990
- console.debug("Subscribing to client_disconnected events");
3991
+ // Clean up previous subscription and handler to prevent
3992
+ // duplicates on reconnection (listener leak fix)
3993
+ if (this._clientDisconnectedSubscription) {
3994
+ try {
3995
+ if (
3996
+ typeof this._clientDisconnectedSubscription.unsubscribe ===
3997
+ "function"
3998
+ ) {
3999
+ this._clientDisconnectedSubscription.unsubscribe();
4000
+ }
4001
+ } catch (e) {
4002
+ // console.debug(`Error unsubscribing old client_disconnected: ${e}`);
4003
+ }
4004
+ this._clientDisconnectedSubscription = null;
4005
+ }
4006
+ if (this._boundHandleClientDisconnected) {
4007
+ try {
4008
+ this.off(
4009
+ "client_disconnected",
4010
+ this._boundHandleClientDisconnected,
4011
+ );
4012
+ } catch (e) {
4013
+ // Handler may not be in list if previous setup was interrupted
4014
+ }
4015
+ this._boundHandleClientDisconnected = null;
4016
+ }
4017
+
4018
+ // console.debug("Subscribing to client_disconnected events");
3991
4019
 
3992
- const handleClientDisconnected = async (event) => {
4020
+ // Store handler at instance level so it can be removed later
4021
+ this._boundHandleClientDisconnected = async (event) => {
3993
4022
  // The client ID is in event.data.id based on the event structure
3994
4023
  const clientId = event.data?.id || event.client;
3995
4024
  const workspace = event.data?.workspace;
3996
4025
  if (clientId && workspace) {
3997
4026
  // Construct the full client path with workspace prefix
3998
4027
  const fullClientId = `${workspace}/${clientId}`;
3999
- console.debug(
4000
- `Client ${fullClientId} disconnected, cleaning up sessions`,
4001
- );
4028
+ // console.debug(
4029
+ // `Client ${fullClientId} disconnected, cleaning up sessions`,
4030
+ // );
4002
4031
  await this._handleClientDisconnected(fullClientId);
4003
4032
  } else if (clientId) {
4004
- console.debug(
4005
- `Client ${clientId} disconnected, cleaning up sessions`,
4006
- );
4033
+ // console.debug(
4034
+ // `Client ${clientId} disconnected, cleaning up sessions`,
4035
+ // );
4007
4036
  await this._handleClientDisconnected(clientId);
4008
4037
  }
4009
4038
  };
@@ -4016,21 +4045,24 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4016
4045
  );
4017
4046
 
4018
4047
  // Then register the local event handler
4019
- this.on("client_disconnected", handleClientDisconnected);
4020
-
4021
- console.debug(
4022
- "Successfully subscribed to client_disconnected events",
4048
+ this.on(
4049
+ "client_disconnected",
4050
+ this._boundHandleClientDisconnected,
4023
4051
  );
4052
+
4053
+ // console.debug(
4054
+ // "Successfully subscribed to client_disconnected events",
4055
+ // );
4024
4056
  } else {
4025
- console.debug(
4026
- "Manager does not support subscribe method, skipping client_disconnected handling",
4027
- );
4057
+ // console.debug(
4058
+ // "Manager does not support subscribe method, skipping client_disconnected handling",
4059
+ // );
4028
4060
  this._clientDisconnectedSubscription = null;
4029
4061
  }
4030
4062
  } catch (subscribeError) {
4031
- console.debug(
4032
- `Failed to subscribe to client_disconnected events: ${subscribeError}`,
4033
- );
4063
+ // console.debug(
4064
+ // `Failed to subscribe to client_disconnected events: ${subscribeError}`,
4065
+ // );
4034
4066
  this._clientDisconnectedSubscription = null;
4035
4067
  }
4036
4068
  } catch (managerError) {
@@ -4327,7 +4359,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4327
4359
  this._boundHandleError = null;
4328
4360
  }
4329
4361
 
4330
- // Clean up client_disconnected subscription
4362
+ // Clean up client_disconnected subscription and handler
4331
4363
  if (this._clientDisconnectedSubscription) {
4332
4364
  try {
4333
4365
  if (
@@ -4336,11 +4368,14 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4336
4368
  this._clientDisconnectedSubscription.unsubscribe();
4337
4369
  }
4338
4370
  } catch (e) {
4339
- console.debug(`Error unsubscribing client_disconnected: ${e}`);
4371
+ // console.debug(`Error unsubscribing client_disconnected: ${e}`);
4340
4372
  }
4341
- this.off("client_disconnected");
4342
4373
  this._clientDisconnectedSubscription = null;
4343
4374
  }
4375
+ if (this._boundHandleClientDisconnected) {
4376
+ this.off("client_disconnected", this._boundHandleClientDisconnected);
4377
+ this._boundHandleClientDisconnected = null;
4378
+ }
4344
4379
 
4345
4380
  // Remove the global unhandled rejection handler
4346
4381
  this._removeRejectionHandler();
@@ -4356,13 +4391,13 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4356
4391
  try {
4357
4392
  task.cancel();
4358
4393
  } catch (e) {
4359
- console.debug(`Error canceling background task: ${e}`);
4394
+ // console.debug(`Error canceling background task: ${e}`);
4360
4395
  }
4361
4396
  }
4362
4397
  }
4363
4398
  this._background_tasks.clear();
4364
4399
  } catch (e) {
4365
- console.debug(`Error cleaning up background tasks: ${e}`);
4400
+ // console.debug(`Error cleaning up background tasks: ${e}`);
4366
4401
  }
4367
4402
 
4368
4403
  // Clear session sweep interval
@@ -4375,11 +4410,11 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4375
4410
  try {
4376
4411
  this._connection = null;
4377
4412
  this._emit_message = function () {
4378
- console.debug("RPC connection closed, ignoring message");
4413
+ // console.debug("RPC connection closed, ignoring message");
4379
4414
  return Promise.reject(new Error("Connection is closed"));
4380
4415
  };
4381
4416
  } catch (e) {
4382
- console.debug(`Error during connection cleanup: ${e}`);
4417
+ // console.debug(`Error during connection cleanup: ${e}`);
4383
4418
  }
4384
4419
 
4385
4420
  this._fire("disconnected");
@@ -4387,15 +4422,15 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4387
4422
 
4388
4423
  async _handleClientDisconnected(clientId) {
4389
4424
  try {
4390
- console.debug(`Handling disconnection for client: ${clientId}`);
4425
+ // console.debug(`Handling disconnection for client: ${clientId}`);
4391
4426
 
4392
4427
  // Clean up all sessions for the disconnected client
4393
4428
  const sessionsCleaned = this._cleanupSessionsForClient(clientId);
4394
4429
 
4395
4430
  if (sessionsCleaned > 0) {
4396
- console.debug(
4397
- `Cleaned up ${sessionsCleaned} sessions for disconnected client: ${clientId}`,
4398
- );
4431
+ // console.debug(
4432
+ // `Cleaned up ${sessionsCleaned} sessions for disconnected client: ${clientId}`,
4433
+ // );
4399
4434
  }
4400
4435
 
4401
4436
  // Fire an event to notify about the client disconnection
@@ -4410,6 +4445,35 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4410
4445
  }
4411
4446
  }
4412
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
+
4413
4477
  _removeFromTargetIdIndex(sessionId) {
4414
4478
  /**
4415
4479
  * Remove a session from the target_id index.
@@ -4444,7 +4508,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4444
4508
  try {
4445
4509
  session.reject(new Error(rejectReason));
4446
4510
  } catch (e) {
4447
- console.debug(`Error rejecting session: ${e}`);
4511
+ // console.debug(`Error rejecting session: ${e}`);
4448
4512
  }
4449
4513
  }
4450
4514
  if (session.heartbeat_task) {
@@ -4483,7 +4547,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4483
4547
  this._cleanupSessionEntry(session, reason);
4484
4548
  delete this._object_store[sessionKey];
4485
4549
  sessionsCleaned++;
4486
- console.debug(`Cleaned up session: ${sessionKey}`);
4550
+ // console.debug(`Cleaned up session: ${sessionKey}`);
4487
4551
  }
4488
4552
 
4489
4553
  delete this._targetIdIndex[clientId];
@@ -4519,7 +4583,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4519
4583
 
4520
4584
  _cleanupOnDisconnect() {
4521
4585
  try {
4522
- console.debug("Cleaning up all sessions due to local RPC disconnection");
4586
+ // console.debug("Cleaning up all sessions due to local RPC disconnection");
4523
4587
 
4524
4588
  const keysToDelete = [];
4525
4589
  for (const key of Object.keys(this._object_store)) {
@@ -4549,7 +4613,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
4549
4613
  try {
4550
4614
  await connection.disconnect();
4551
4615
  } catch (e) {
4552
- console.debug(`Error disconnecting underlying connection: ${e}`);
4616
+ // console.debug(`Error disconnecting underlying connection: ${e}`);
4553
4617
  }
4554
4618
  }
4555
4619
  }
@@ -5043,14 +5107,14 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5043
5107
  * Clean session management - all logic in one place.
5044
5108
  */
5045
5109
  if (!session_id) {
5046
- console.debug("Cannot cleanup session: session_id is empty");
5110
+ // console.debug("Cannot cleanup session: session_id is empty");
5047
5111
  return;
5048
5112
  }
5049
5113
 
5050
5114
  try {
5051
5115
  const store = this._get_session_store(session_id, false);
5052
5116
  if (!store) {
5053
- console.debug(`Session ${session_id} not found for cleanup`);
5117
+ // console.debug(`Session ${session_id} not found for cleanup`);
5054
5118
  return;
5055
5119
  }
5056
5120
 
@@ -5068,9 +5132,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5068
5132
  promise_manager.settle();
5069
5133
  }
5070
5134
  should_cleanup = true;
5071
- console.debug(
5072
- `Promise session ${session_id} settled and marked for cleanup`,
5073
- );
5135
+ // console.debug(
5136
+ // `Promise session ${session_id} settled and marked for cleanup`,
5137
+ // );
5074
5138
  }
5075
5139
  } catch (e) {
5076
5140
  console.warn(
@@ -5088,9 +5152,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5088
5152
  Object.keys(store._callbacks).includes(callback_name)
5089
5153
  ) {
5090
5154
  should_cleanup = true;
5091
- console.debug(
5092
- `Regular session ${session_id} marked for cleanup after ${callback_name}`,
5093
- );
5155
+ // console.debug(
5156
+ // `Regular session ${session_id} marked for cleanup after ${callback_name}`,
5157
+ // );
5094
5158
  }
5095
5159
  }
5096
5160
 
@@ -5112,7 +5176,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5112
5176
 
5113
5177
  const store = this._get_session_store(session_id, false);
5114
5178
  if (!store) {
5115
- console.debug(`Session ${session_id} already cleaned up`);
5179
+ // console.debug(`Session ${session_id} already cleaned up`);
5116
5180
  return;
5117
5181
  }
5118
5182
 
@@ -5154,9 +5218,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5154
5218
  for (let i = 0; i < levels.length - 1; i++) {
5155
5219
  const level = levels[i];
5156
5220
  if (!current_store[level]) {
5157
- console.debug(
5158
- `Session path ${session_id} not found at level ${level}`,
5159
- );
5221
+ // console.debug(
5222
+ // `Session path ${session_id} not found at level ${level}`,
5223
+ // );
5160
5224
  return;
5161
5225
  }
5162
5226
  current_store = current_store[level];
@@ -5166,7 +5230,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5166
5230
  const final_key = levels[levels.length - 1];
5167
5231
  if (current_store[final_key]) {
5168
5232
  delete current_store[final_key];
5169
- console.debug(`Cleaned up session ${session_id}`);
5233
+ // console.debug(`Cleaned up session ${session_id}`);
5170
5234
 
5171
5235
  // Clean up empty parent containers
5172
5236
  this._cleanup_empty_containers(levels.slice(0, -1));
@@ -5204,9 +5268,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5204
5268
  Object.keys(container).length === 0
5205
5269
  ) {
5206
5270
  delete current_store[container_key];
5207
- console.debug(
5208
- `Cleaned up empty container at depth ${depth}: ${path_levels.slice(0, depth + 1).join(".")}`,
5209
- );
5271
+ // console.debug(
5272
+ // `Cleaned up empty container at depth ${depth}: ${path_levels.slice(0, depth + 1).join(".")}`,
5273
+ // );
5210
5274
  } else {
5211
5275
  // Container is not empty, stop cleanup
5212
5276
  break;
@@ -5281,7 +5345,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5281
5345
  * Force cleanup all sessions (for testing purposes).
5282
5346
  */
5283
5347
  if (!this._object_store) {
5284
- console.debug("Force cleaning up 0 sessions");
5348
+ // console.debug("Force cleaning up 0 sessions");
5285
5349
  return;
5286
5350
  }
5287
5351
 
@@ -5311,7 +5375,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5311
5375
  // Clear the target_id index since all sessions are removed
5312
5376
  this._targetIdIndex = {};
5313
5377
 
5314
- console.debug(`Force cleaning up ${cleaned_count} sessions`);
5378
+ // console.debug(`Force cleaning up ${cleaned_count} sessions`);
5315
5379
  }
5316
5380
 
5317
5381
  _sweepStaleSessions() {
@@ -5347,7 +5411,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5347
5411
  }
5348
5412
  }
5349
5413
  if (swept > 0) {
5350
- console.debug(`Swept ${swept} stale session(s)`);
5414
+ // console.debug(`Swept ${swept} stale session(s)`);
5351
5415
  }
5352
5416
  }
5353
5417
 
@@ -5369,7 +5433,7 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5369
5433
  },
5370
5434
  settle: () => {
5371
5435
  // Promise is settled (resolved or rejected)
5372
- console.debug("Promise settled");
5436
+ // console.debug("Promise settled");
5373
5437
  },
5374
5438
  };
5375
5439
  }
@@ -5640,9 +5704,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5640
5704
  // Clean up target_id index before deleting the session
5641
5705
  self._removeFromTargetIdIndex(local_session_id);
5642
5706
  delete self._object_store[local_session_id];
5643
- console.debug(
5644
- `Cleaned up session ${local_session_id} after timeout`,
5645
- );
5707
+ // console.debug(
5708
+ // `Cleaned up session ${local_session_id} after timeout`,
5709
+ // );
5646
5710
  }
5647
5711
  };
5648
5712
 
@@ -5925,9 +5989,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5925
5989
  } catch (e) {
5926
5990
  // Clean promise method detection - TYPE-BASED, not string-based
5927
5991
  if (this._is_promise_method_call(data["method"])) {
5928
- console.debug(
5929
- `Promise method ${data["method"]} not available (detected by session type), ignoring: ${method_name}`,
5930
- );
5992
+ // console.debug(
5993
+ // `Promise method ${data["method"]} not available (detected by session type), ignoring: ${method_name}`,
5994
+ // );
5931
5995
  return;
5932
5996
  }
5933
5997
 
@@ -5937,18 +6001,18 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5937
6001
  const session_id = method_parts[0];
5938
6002
  // Check if the session exists but the specific method doesn't
5939
6003
  if (session_id in this._object_store) {
5940
- console.debug(
5941
- `Session ${session_id} exists but method ${data["method"]} not found, likely expired callback: ${method_name}`,
5942
- );
6004
+ // console.debug(
6005
+ // `Session ${session_id} exists but method ${data["method"]} not found, likely expired callback: ${method_name}`,
6006
+ // );
5943
6007
  // For expired callbacks, don't throw an exception, just log and return
5944
6008
  if (typeof reject === "function") {
5945
6009
  reject(new Error(`Method expired or not found: ${method_name}`));
5946
6010
  }
5947
6011
  return;
5948
6012
  } else {
5949
- console.debug(
5950
- `Session ${session_id} not found for method ${data["method"]}, likely cleaned up: ${method_name}`,
5951
- );
6013
+ // console.debug(
6014
+ // `Session ${session_id} not found for method ${data["method"]}, likely cleaned up: ${method_name}`,
6015
+ // );
5952
6016
  // For cleaned up sessions, just log and return without throwing
5953
6017
  if (typeof reject === "function") {
5954
6018
  reject(new Error(`Session not found: ${method_name}`));
@@ -5957,9 +6021,9 @@ class RPC extends _utils_index_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
5957
6021
  }
5958
6022
  }
5959
6023
 
5960
- console.debug(
5961
- `Failed to find method ${method_name} at ${this._client_id}`,
5962
- );
6024
+ // console.debug(
6025
+ // `Failed to find method ${method_name} at ${this._client_id}`,
6026
+ // );
5963
6027
  const error = new Error(
5964
6028
  `Method not found: ${method_name} at ${this._client_id}`,
5965
6029
  );
@@ -9902,6 +9966,7 @@ __webpack_require__.r(__webpack_exports__);
9902
9966
  /* harmony export */ HTTPStreamingRPCConnection: () => (/* reexport safe */ _http_client_js__WEBPACK_IMPORTED_MODULE_5__.HTTPStreamingRPCConnection),
9903
9967
  /* harmony export */ LocalWebSocket: () => (/* binding */ LocalWebSocket),
9904
9968
  /* harmony export */ RPC: () => (/* reexport safe */ _rpc_js__WEBPACK_IMPORTED_MODULE_0__.RPC),
9969
+ /* harmony export */ WebsocketRPCConnection: () => (/* binding */ WebsocketRPCConnection),
9905
9970
  /* harmony export */ connectToServer: () => (/* binding */ connectToServer),
9906
9971
  /* harmony export */ connectToServerHTTP: () => (/* reexport safe */ _http_client_js__WEBPACK_IMPORTED_MODULE_5__.connectToServerHTTP),
9907
9972
  /* harmony export */ decryptPayload: () => (/* reexport safe */ _crypto_js__WEBPACK_IMPORTED_MODULE_1__.decryptPayload),
@@ -9983,6 +10048,7 @@ class WebsocketRPCConnection {
9983
10048
  this._reconnect_timeouts = new Set(); // Track reconnection timeouts
9984
10049
  this._additional_headers = additional_headers;
9985
10050
  this._reconnecting = false; // Mutex to prevent overlapping reconnection attempts
10051
+ this._closedDuringReconnect = false; // Flag for close events during reconnection
9986
10052
  this._disconnectedNotified = false;
9987
10053
  }
9988
10054
 
@@ -10225,7 +10291,9 @@ class WebsocketRPCConnection {
10225
10291
  this._websocket.onclose = this._handle_close.bind(this);
10226
10292
 
10227
10293
  if (this._handle_connected) {
10228
- 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);
10229
10297
  }
10230
10298
  return this.connection_info;
10231
10299
  } catch (error) {
@@ -10291,9 +10359,10 @@ class WebsocketRPCConnection {
10291
10359
  // Notify the RPC layer immediately so it can reject pending calls
10292
10360
  this._notifyDisconnected(event.reason);
10293
10361
 
10294
- // 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.
10295
10364
  if (this._reconnecting) {
10296
- console.debug("Reconnection already in progress, skipping");
10365
+ this._closedDuringReconnect = true;
10297
10366
  return;
10298
10367
  }
10299
10368
  this._reconnecting = true;
@@ -10315,6 +10384,10 @@ class WebsocketRPCConnection {
10315
10384
  console.warn(
10316
10385
  `Reconnecting to ${this._server_url.split("?")[0]} (attempt #${retry})`,
10317
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
+
10318
10391
  // Open the connection, this will trigger the on_connected callback
10319
10392
  await this.open();
10320
10393
 
@@ -10323,6 +10396,22 @@ class WebsocketRPCConnection {
10323
10396
  // which includes re-registering all services to the server
10324
10397
  await new Promise((resolve) => setTimeout(resolve, 500));
10325
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
+
10326
10415
  console.warn(
10327
10416
  `Successfully reconnected to server ${this._server_url} (services re-registered)`,
10328
10417
  );
@@ -10366,9 +10455,7 @@ class WebsocketRPCConnection {
10366
10455
  const jitter = (Math.random() * 2 - 1) * maxJitter * delay;
10367
10456
  const finalDelay = Math.max(100, delay + jitter);
10368
10457
 
10369
- console.debug(
10370
- `Waiting ${(finalDelay / 1000).toFixed(2)}s before next reconnection attempt`,
10371
- );
10458
+ // console.debug(`Waiting ${(finalDelay / 1000).toFixed(2)}s before next reconnection attempt`);
10372
10459
 
10373
10460
  // Track the reconnection timeout to prevent leaks
10374
10461
  const timeoutId = setTimeout(async () => {
@@ -10433,6 +10520,7 @@ class WebsocketRPCConnection {
10433
10520
  disconnect(reason) {
10434
10521
  this._closed = true;
10435
10522
  this._reconnecting = false;
10523
+ this._closedDuringReconnect = false;
10436
10524
  // Ensure websocket is closed if it exists and is not already closed or closing
10437
10525
  if (
10438
10526
  this._websocket &&
@@ -11107,6 +11195,8 @@ class LocalWebSocket {
11107
11195
  // hypha-core's deno build does: import { hyphaWebsocketClient } from 'hypha-rpc'
11108
11196
  // The UMD build wraps everything under this name via webpack's `library` option,
11109
11197
  // but the ESM build exports flat, so we need this explicit re-export.
11198
+
11199
+
11110
11200
  const hyphaWebsocketClient = {
11111
11201
  RPC: _rpc_js__WEBPACK_IMPORTED_MODULE_0__.RPC,
11112
11202
  API_VERSION: _rpc_js__WEBPACK_IMPORTED_MODULE_0__.API_VERSION,