hypha-rpc 0.20.48 → 0.20.50
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/dist/hypha-rpc-websocket.js +158 -16
- 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 +158 -16
- package/dist/hypha-rpc-websocket.mjs.map +1 -1
- package/package.json +1 -1
- package/src/rpc.js +98 -8
- package/src/utils/index.js +34 -0
- package/src/webrtc-client.js +9 -1
- package/src/websocket-client.js +17 -7
- package/tests/utils_test.js +66 -1
- package/tests/websocket_client_test.js +126 -2
- package/report.html +0 -39
|
@@ -548,22 +548,24 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
548
548
|
|
|
549
549
|
async get_manager_service(config) {
|
|
550
550
|
config = config || {};
|
|
551
|
-
|
|
551
|
+
|
|
552
552
|
// Add retry logic
|
|
553
553
|
const maxRetries = 20;
|
|
554
554
|
const retryDelay = 500; // 500ms
|
|
555
|
-
|
|
555
|
+
|
|
556
556
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
557
557
|
if (!this._connection.manager_id) {
|
|
558
558
|
if (attempt < maxRetries - 1) {
|
|
559
|
-
console.warn(
|
|
560
|
-
|
|
559
|
+
console.warn(
|
|
560
|
+
`Manager ID not set, retrying in ${retryDelay}ms (attempt ${attempt + 1}/${maxRetries})`,
|
|
561
|
+
);
|
|
562
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
561
563
|
continue;
|
|
562
564
|
} else {
|
|
563
565
|
throw new Error("Manager ID not set after maximum retries");
|
|
564
566
|
}
|
|
565
567
|
}
|
|
566
|
-
|
|
568
|
+
|
|
567
569
|
try {
|
|
568
570
|
const svc = await this.get_remote_service(
|
|
569
571
|
`*/${this._connection.manager_id}:default`,
|
|
@@ -572,8 +574,10 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
572
574
|
return svc;
|
|
573
575
|
} catch (e) {
|
|
574
576
|
if (attempt < maxRetries - 1) {
|
|
575
|
-
console.warn(
|
|
576
|
-
|
|
577
|
+
console.warn(
|
|
578
|
+
`Failed to get manager service, retrying in ${retryDelay}ms: ${e.message}`,
|
|
579
|
+
);
|
|
580
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
577
581
|
} else {
|
|
578
582
|
throw e;
|
|
579
583
|
}
|
|
@@ -1519,7 +1523,59 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
1519
1523
|
return bObject;
|
|
1520
1524
|
}
|
|
1521
1525
|
|
|
1522
|
-
if (
|
|
1526
|
+
if ((0,_utils__WEBPACK_IMPORTED_MODULE_0__.isGenerator)(aObject) || (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isAsyncGenerator)(aObject)) {
|
|
1527
|
+
// Handle generator functions and generator objects
|
|
1528
|
+
(0,_utils__WEBPACK_IMPORTED_MODULE_0__.assert)(
|
|
1529
|
+
session_id && typeof session_id === "string",
|
|
1530
|
+
"Session ID is required for generator encoding",
|
|
1531
|
+
);
|
|
1532
|
+
const object_id = (0,_utils__WEBPACK_IMPORTED_MODULE_0__.randId)();
|
|
1533
|
+
|
|
1534
|
+
// Get the session store
|
|
1535
|
+
const store = this._get_session_store(session_id, true);
|
|
1536
|
+
(0,_utils__WEBPACK_IMPORTED_MODULE_0__.assert)(
|
|
1537
|
+
store !== null,
|
|
1538
|
+
`Failed to create session store ${session_id} due to invalid parent`,
|
|
1539
|
+
);
|
|
1540
|
+
|
|
1541
|
+
// Check if it's an async generator
|
|
1542
|
+
const isAsync = (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isAsyncGenerator)(aObject);
|
|
1543
|
+
|
|
1544
|
+
// Define method to get next item from the generator
|
|
1545
|
+
const nextItemMethod = async () => {
|
|
1546
|
+
if (isAsync) {
|
|
1547
|
+
const iterator = aObject;
|
|
1548
|
+
const result = await iterator.next();
|
|
1549
|
+
if (result.done) {
|
|
1550
|
+
delete store[object_id];
|
|
1551
|
+
return { _rtype: "stop_iteration" };
|
|
1552
|
+
}
|
|
1553
|
+
return result.value;
|
|
1554
|
+
} else {
|
|
1555
|
+
const iterator = aObject;
|
|
1556
|
+
const result = iterator.next();
|
|
1557
|
+
if (result.done) {
|
|
1558
|
+
delete store[object_id];
|
|
1559
|
+
return { _rtype: "stop_iteration" };
|
|
1560
|
+
}
|
|
1561
|
+
return result.value;
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1565
|
+
// Store the next_item method in the session
|
|
1566
|
+
store[object_id] = nextItemMethod;
|
|
1567
|
+
|
|
1568
|
+
// Create a method that will be used to fetch the next item from the generator
|
|
1569
|
+
bObject = {
|
|
1570
|
+
_rtype: "generator",
|
|
1571
|
+
_rserver: this._server_base_url,
|
|
1572
|
+
_rtarget: this._client_id,
|
|
1573
|
+
_rmethod: `${session_id}.${object_id}`,
|
|
1574
|
+
_rpromise: "*",
|
|
1575
|
+
_rdoc: "Remote generator",
|
|
1576
|
+
};
|
|
1577
|
+
return bObject;
|
|
1578
|
+
} else if (typeof aObject === "function") {
|
|
1523
1579
|
if (this._method_annotations.has(aObject)) {
|
|
1524
1580
|
let annotation = this._method_annotations.get(aObject);
|
|
1525
1581
|
bObject = {
|
|
@@ -1765,6 +1821,38 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
1765
1821
|
remote_workspace,
|
|
1766
1822
|
local_workspace,
|
|
1767
1823
|
);
|
|
1824
|
+
} else if (aObject._rtype === "generator") {
|
|
1825
|
+
// Create a method to fetch next items from the remote generator
|
|
1826
|
+
const gen_method = this._generate_remote_method(
|
|
1827
|
+
aObject,
|
|
1828
|
+
remote_parent,
|
|
1829
|
+
local_parent,
|
|
1830
|
+
remote_workspace,
|
|
1831
|
+
local_workspace,
|
|
1832
|
+
);
|
|
1833
|
+
|
|
1834
|
+
// Create an async generator proxy
|
|
1835
|
+
async function* asyncGeneratorProxy() {
|
|
1836
|
+
try {
|
|
1837
|
+
while (true) {
|
|
1838
|
+
try {
|
|
1839
|
+
const next_item = await gen_method();
|
|
1840
|
+
// Check for StopIteration signal
|
|
1841
|
+
if (next_item && next_item._rtype === "stop_iteration") {
|
|
1842
|
+
break;
|
|
1843
|
+
}
|
|
1844
|
+
yield next_item;
|
|
1845
|
+
} catch (error) {
|
|
1846
|
+
console.error("Error in generator:", error);
|
|
1847
|
+
throw error;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
} catch (error) {
|
|
1851
|
+
console.error("Error in generator:", error);
|
|
1852
|
+
throw error;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
bObject = asyncGeneratorProxy();
|
|
1768
1856
|
} else if (aObject._rtype === "ndarray") {
|
|
1769
1857
|
/*global nj tf*/
|
|
1770
1858
|
//create build array/tensor if used in the plugin
|
|
@@ -1935,6 +2023,8 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
1935
2023
|
/* harmony export */ convertCase: () => (/* binding */ convertCase),
|
|
1936
2024
|
/* harmony export */ dtypeToTypedArray: () => (/* binding */ dtypeToTypedArray),
|
|
1937
2025
|
/* harmony export */ expandKwargs: () => (/* binding */ expandKwargs),
|
|
2026
|
+
/* harmony export */ isAsyncGenerator: () => (/* binding */ isAsyncGenerator),
|
|
2027
|
+
/* harmony export */ isGenerator: () => (/* binding */ isGenerator),
|
|
1938
2028
|
/* harmony export */ loadRequirements: () => (/* binding */ loadRequirements),
|
|
1939
2029
|
/* harmony export */ loadRequirementsInWebworker: () => (/* binding */ loadRequirementsInWebworker),
|
|
1940
2030
|
/* harmony export */ loadRequirementsInWindow: () => (/* binding */ loadRequirementsInWindow),
|
|
@@ -2456,6 +2546,40 @@ class Semaphore {
|
|
|
2456
2546
|
}
|
|
2457
2547
|
}
|
|
2458
2548
|
|
|
2549
|
+
/**
|
|
2550
|
+
* Check if the object is a generator
|
|
2551
|
+
* @param {Object} obj - Object to check
|
|
2552
|
+
* @returns {boolean} - True if the object is a generator
|
|
2553
|
+
*/
|
|
2554
|
+
function isGenerator(obj) {
|
|
2555
|
+
if (!obj) return false;
|
|
2556
|
+
|
|
2557
|
+
return (
|
|
2558
|
+
typeof obj === "object" &&
|
|
2559
|
+
typeof obj.next === "function" &&
|
|
2560
|
+
typeof obj.throw === "function" &&
|
|
2561
|
+
typeof obj.return === "function"
|
|
2562
|
+
);
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
/**
|
|
2566
|
+
* Check if an object is an async generator object
|
|
2567
|
+
* @param {any} obj - Object to check
|
|
2568
|
+
* @returns {boolean} True if object is an async generator object
|
|
2569
|
+
*/
|
|
2570
|
+
function isAsyncGenerator(obj) {
|
|
2571
|
+
if (!obj) return false;
|
|
2572
|
+
// Check if it's an async generator object
|
|
2573
|
+
return (
|
|
2574
|
+
typeof obj === "object" &&
|
|
2575
|
+
typeof obj.next === "function" &&
|
|
2576
|
+
typeof obj.throw === "function" &&
|
|
2577
|
+
typeof obj.return === "function" &&
|
|
2578
|
+
Symbol.asyncIterator in Object(obj) &&
|
|
2579
|
+
obj[Symbol.toStringTag] === "AsyncGenerator"
|
|
2580
|
+
);
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2459
2583
|
|
|
2460
2584
|
/***/ }),
|
|
2461
2585
|
|
|
@@ -2522,7 +2646,13 @@ class WebRTCConnection {
|
|
|
2522
2646
|
this._handle_disconnected = null;
|
|
2523
2647
|
this._handle_connected = () => {};
|
|
2524
2648
|
this.manager_id = null;
|
|
2649
|
+
this._last_message = null;
|
|
2525
2650
|
this._data_channel.onopen = async () => {
|
|
2651
|
+
if (this._last_message) {
|
|
2652
|
+
console.info("Resending last message after connection established");
|
|
2653
|
+
this._data_channel.send(this._last_message);
|
|
2654
|
+
this._last_message = null;
|
|
2655
|
+
}
|
|
2526
2656
|
this._handle_connected &&
|
|
2527
2657
|
this._handle_connected({ channel: this._data_channel });
|
|
2528
2658
|
};
|
|
@@ -2557,15 +2687,17 @@ class WebRTCConnection {
|
|
|
2557
2687
|
async emit_message(data) {
|
|
2558
2688
|
(0,_utils__WEBPACK_IMPORTED_MODULE_1__.assert)(this._handle_message, "No handler for message");
|
|
2559
2689
|
try {
|
|
2690
|
+
this._last_message = data;
|
|
2560
2691
|
this._data_channel.send(data);
|
|
2692
|
+
this._last_message = null;
|
|
2561
2693
|
} catch (exp) {
|
|
2562
|
-
// data = msgpack_unpackb(data);
|
|
2563
2694
|
console.error(`Failed to send data, error: ${exp}`);
|
|
2564
2695
|
throw exp;
|
|
2565
2696
|
}
|
|
2566
2697
|
}
|
|
2567
2698
|
|
|
2568
2699
|
async disconnect(reason) {
|
|
2700
|
+
this._last_message = null;
|
|
2569
2701
|
this._data_channel = null;
|
|
2570
2702
|
console.info(`data channel connection disconnected (${reason})`);
|
|
2571
2703
|
}
|
|
@@ -4800,6 +4932,7 @@ class WebsocketRPCConnection {
|
|
|
4800
4932
|
this._token_refresh_interval = token_refresh_interval;
|
|
4801
4933
|
this.manager_id = null;
|
|
4802
4934
|
this._refresh_token_task = null;
|
|
4935
|
+
this._last_message = null; // Store the last sent message
|
|
4803
4936
|
}
|
|
4804
4937
|
|
|
4805
4938
|
on_message(handler) {
|
|
@@ -5038,6 +5171,12 @@ class WebsocketRPCConnection {
|
|
|
5038
5171
|
`Reconnecting to ${this._server_url.split("?")[0]} (attempt #${retry})`,
|
|
5039
5172
|
);
|
|
5040
5173
|
await this.open();
|
|
5174
|
+
// Resend last message if there was one
|
|
5175
|
+
if (this._last_message) {
|
|
5176
|
+
console.info("Resending last message after reconnection");
|
|
5177
|
+
this._websocket.send(this._last_message);
|
|
5178
|
+
this._last_message = null;
|
|
5179
|
+
}
|
|
5041
5180
|
console.warn(
|
|
5042
5181
|
`Successfully reconnected to server ${this._server_url}`,
|
|
5043
5182
|
);
|
|
@@ -5083,7 +5222,9 @@ class WebsocketRPCConnection {
|
|
|
5083
5222
|
await this.open();
|
|
5084
5223
|
}
|
|
5085
5224
|
try {
|
|
5225
|
+
this._last_message = data; // Store the message before sending
|
|
5086
5226
|
this._websocket.send(data);
|
|
5227
|
+
this._last_message = null; // Clear after successful send
|
|
5087
5228
|
} catch (exp) {
|
|
5088
5229
|
console.error(`Failed to send data, error: ${exp}`);
|
|
5089
5230
|
throw exp;
|
|
@@ -5092,6 +5233,7 @@ class WebsocketRPCConnection {
|
|
|
5092
5233
|
|
|
5093
5234
|
disconnect(reason) {
|
|
5094
5235
|
this._closed = true;
|
|
5236
|
+
this._last_message = null; // Clear last message on disconnect
|
|
5095
5237
|
if (this._websocket && this._websocket.readyState === WebSocket.OPEN) {
|
|
5096
5238
|
this._websocket.close(1000, reason);
|
|
5097
5239
|
}
|
|
@@ -5213,20 +5355,20 @@ async function connectToServer(config) {
|
|
|
5213
5355
|
"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.",
|
|
5214
5356
|
);
|
|
5215
5357
|
// wait for 0.5 seconds
|
|
5216
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
5358
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
5217
5359
|
// Ensure manager_id is set before proceeding
|
|
5218
5360
|
if (!connection.manager_id) {
|
|
5219
5361
|
console.warn("Manager ID not set immediately, waiting...");
|
|
5220
|
-
|
|
5362
|
+
|
|
5221
5363
|
// Wait for manager_id to be set with timeout
|
|
5222
5364
|
const maxWaitTime = 5000; // 5 seconds
|
|
5223
5365
|
const checkInterval = 100; // 100ms
|
|
5224
5366
|
const startTime = Date.now();
|
|
5225
|
-
|
|
5226
|
-
while (!connection.manager_id &&
|
|
5227
|
-
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
5367
|
+
|
|
5368
|
+
while (!connection.manager_id && Date.now() - startTime < maxWaitTime) {
|
|
5369
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
5228
5370
|
}
|
|
5229
|
-
|
|
5371
|
+
|
|
5230
5372
|
if (!connection.manager_id) {
|
|
5231
5373
|
console.error("Manager ID still not set after waiting");
|
|
5232
5374
|
throw new Error("Failed to get manager ID from server");
|
|
@@ -5239,7 +5381,7 @@ async function connectToServer(config) {
|
|
|
5239
5381
|
`Connected to the wrong workspace: ${connection_info.workspace}, expected: ${config.workspace}`,
|
|
5240
5382
|
);
|
|
5241
5383
|
}
|
|
5242
|
-
|
|
5384
|
+
|
|
5243
5385
|
const workspace = connection_info.workspace;
|
|
5244
5386
|
const rpc = new _rpc_js__WEBPACK_IMPORTED_MODULE_0__.RPC(connection, {
|
|
5245
5387
|
client_id: clientId,
|