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.
- package/coverage/html/index.html +1 -1
- package/dist/hypha-rpc-websocket.js +173 -83
- package/dist/hypha-rpc-websocket.js.map +1 -1
- package/dist/hypha-rpc-websocket.min.js +1 -1
- package/dist/hypha-rpc-websocket.min.js.map +1 -1
- package/dist/hypha-rpc-websocket.min.mjs +1 -1
- package/dist/hypha-rpc-websocket.min.mjs.map +1 -1
- package/dist/hypha-rpc-websocket.mjs +175 -84
- package/dist/hypha-rpc-websocket.mjs.map +1 -1
- package/package.json +1 -1
- package/src/http-client.js +1 -1
- package/src/rpc.js +140 -76
- package/src/websocket-client.js +31 -6
package/coverage/html/index.html
CHANGED
|
@@ -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-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
4020
|
-
|
|
4021
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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,
|