hypha-rpc 0.20.56 → 0.20.58
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 +215 -98
- 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 +215 -98
- package/dist/hypha-rpc-websocket.mjs.map +1 -1
- package/package.json +1 -1
- package/src/rpc.js +53 -30
- package/src/webrtc-client.js +162 -68
|
@@ -960,10 +960,14 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
960
960
|
description,
|
|
961
961
|
) {
|
|
962
962
|
let store = this._get_session_store(session_id, true);
|
|
963
|
-
(
|
|
964
|
-
store
|
|
965
|
-
|
|
966
|
-
|
|
963
|
+
if (!store) {
|
|
964
|
+
// Handle the case where session store creation failed gracefully
|
|
965
|
+
console.warn(
|
|
966
|
+
`Failed to create session store ${session_id}, session management may be impaired`,
|
|
967
|
+
);
|
|
968
|
+
// Create a minimal fallback store
|
|
969
|
+
store = {};
|
|
970
|
+
}
|
|
967
971
|
let encoded = {};
|
|
968
972
|
|
|
969
973
|
if (timer && reject && this._method_timeout) {
|
|
@@ -1043,7 +1047,19 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
1043
1047
|
}
|
|
1044
1048
|
|
|
1045
1049
|
// Wait for all chunk uploads to finish
|
|
1046
|
-
|
|
1050
|
+
try {
|
|
1051
|
+
await Promise.all(tasks);
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
// If any chunk fails, clean up the message cache
|
|
1054
|
+
try {
|
|
1055
|
+
await message_cache.remove(message_id);
|
|
1056
|
+
} catch (cleanupError) {
|
|
1057
|
+
console.error(
|
|
1058
|
+
`Failed to clean up message cache after error: ${cleanupError}`,
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
throw error;
|
|
1062
|
+
}
|
|
1047
1063
|
} else {
|
|
1048
1064
|
// 3) Legacy version (sequential appends):
|
|
1049
1065
|
await message_cache.create(message_id, !!session_id);
|
|
@@ -1181,13 +1197,24 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
1181
1197
|
);
|
|
1182
1198
|
// By default, hypha will clear the session after the method is called
|
|
1183
1199
|
// However, if the args contains _rintf === true, we will not clear the session
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1200
|
+
|
|
1201
|
+
// Helper function to recursively check for _rintf objects
|
|
1202
|
+
function hasInterfaceObject(obj) {
|
|
1203
|
+
if (!obj || typeof obj !== "object") return false;
|
|
1204
|
+
if (obj._rintf === true) return true;
|
|
1205
|
+
if (Array.isArray(obj)) {
|
|
1206
|
+
return obj.some((item) => hasInterfaceObject(item));
|
|
1207
|
+
}
|
|
1208
|
+
if (obj.constructor === Object) {
|
|
1209
|
+
return Object.values(obj).some((value) =>
|
|
1210
|
+
hasInterfaceObject(value),
|
|
1211
|
+
);
|
|
1189
1212
|
}
|
|
1213
|
+
return false;
|
|
1190
1214
|
}
|
|
1215
|
+
|
|
1216
|
+
let clear_after_called = !hasInterfaceObject(args);
|
|
1217
|
+
|
|
1191
1218
|
const promiseData = await self._encode_promise(
|
|
1192
1219
|
resolve,
|
|
1193
1220
|
reject,
|
|
@@ -1478,7 +1505,8 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
1478
1505
|
const last_index = levels.length - 1;
|
|
1479
1506
|
for (let level of levels.slice(0, last_index)) {
|
|
1480
1507
|
if (!store[level]) {
|
|
1481
|
-
|
|
1508
|
+
// Instead of returning null, create intermediate sessions as needed
|
|
1509
|
+
store[level] = {};
|
|
1482
1510
|
}
|
|
1483
1511
|
store = store[level];
|
|
1484
1512
|
}
|
|
@@ -1692,6 +1720,9 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
1692
1720
|
nj.NdArray &&
|
|
1693
1721
|
aObject instanceof nj.NdArray
|
|
1694
1722
|
) {
|
|
1723
|
+
if (!aObject.selection || !aObject.selection.data) {
|
|
1724
|
+
throw new Error("Invalid NumJS array: missing selection or data");
|
|
1725
|
+
}
|
|
1695
1726
|
const dtype = (0,_utils__WEBPACK_IMPORTED_MODULE_0__.typedArrayToDtype)(aObject.selection.data);
|
|
1696
1727
|
bObject = {
|
|
1697
1728
|
_rtype: "ndarray",
|
|
@@ -1784,10 +1815,7 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
1784
1815
|
local_workspace,
|
|
1785
1816
|
),
|
|
1786
1817
|
};
|
|
1787
|
-
} else if (
|
|
1788
|
-
aObject.constructor instanceof Object ||
|
|
1789
|
-
Array.isArray(aObject)
|
|
1790
|
-
) {
|
|
1818
|
+
} else if (aObject.constructor === Object || Array.isArray(aObject)) {
|
|
1791
1819
|
bObject = isarray ? [] : {};
|
|
1792
1820
|
const keys = Object.keys(aObject);
|
|
1793
1821
|
for (let k of keys) {
|
|
@@ -1861,23 +1889,18 @@ class RPC extends _utils__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
|
|
|
1861
1889
|
|
|
1862
1890
|
// Create an async generator proxy
|
|
1863
1891
|
async function* asyncGeneratorProxy() {
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
break;
|
|
1871
|
-
}
|
|
1872
|
-
yield next_item;
|
|
1873
|
-
} catch (error) {
|
|
1874
|
-
console.error("Error in generator:", error);
|
|
1875
|
-
throw error;
|
|
1892
|
+
while (true) {
|
|
1893
|
+
try {
|
|
1894
|
+
const next_item = await gen_method();
|
|
1895
|
+
// Check for StopIteration signal
|
|
1896
|
+
if (next_item && next_item._rtype === "stop_iteration") {
|
|
1897
|
+
break;
|
|
1876
1898
|
}
|
|
1899
|
+
yield next_item;
|
|
1900
|
+
} catch (error) {
|
|
1901
|
+
console.error("Error in generator:", error);
|
|
1902
|
+
throw error;
|
|
1877
1903
|
}
|
|
1878
|
-
} catch (error) {
|
|
1879
|
-
console.error("Error in generator:", error);
|
|
1880
|
-
throw error;
|
|
1881
1904
|
}
|
|
1882
1905
|
}
|
|
1883
1906
|
bObject = asyncGeneratorProxy();
|
|
@@ -2791,6 +2814,21 @@ async function _createOffer(params, server, config, onInit, context) {
|
|
|
2791
2814
|
let answer = await pc.createAnswer();
|
|
2792
2815
|
await pc.setLocalDescription(answer);
|
|
2793
2816
|
|
|
2817
|
+
// Wait for ICE candidates to be gathered (important for Firefox)
|
|
2818
|
+
await new Promise((resolveIce) => {
|
|
2819
|
+
if (pc.iceGatheringState === "complete") {
|
|
2820
|
+
resolveIce();
|
|
2821
|
+
} else {
|
|
2822
|
+
pc.addEventListener("icegatheringstatechange", () => {
|
|
2823
|
+
if (pc.iceGatheringState === "complete") {
|
|
2824
|
+
resolveIce();
|
|
2825
|
+
}
|
|
2826
|
+
});
|
|
2827
|
+
// Don't wait forever for ICE gathering
|
|
2828
|
+
setTimeout(resolveIce, 5000);
|
|
2829
|
+
}
|
|
2830
|
+
});
|
|
2831
|
+
|
|
2794
2832
|
return {
|
|
2795
2833
|
sdp: pc.localDescription.sdp,
|
|
2796
2834
|
type: pc.localDescription.type,
|
|
@@ -2810,30 +2848,80 @@ async function getRTCService(server, service_id, config) {
|
|
|
2810
2848
|
});
|
|
2811
2849
|
|
|
2812
2850
|
return new Promise(async (resolve, reject) => {
|
|
2851
|
+
let resolved = false;
|
|
2852
|
+
const timeout = setTimeout(() => {
|
|
2853
|
+
if (!resolved) {
|
|
2854
|
+
resolved = true;
|
|
2855
|
+
pc.close();
|
|
2856
|
+
reject(new Error("WebRTC Connection timeout"));
|
|
2857
|
+
}
|
|
2858
|
+
}, 30000); // Increase timeout to 30 seconds
|
|
2859
|
+
|
|
2813
2860
|
try {
|
|
2814
2861
|
pc.addEventListener(
|
|
2815
2862
|
"connectionstatechange",
|
|
2816
2863
|
() => {
|
|
2864
|
+
console.log("WebRTC Connection state: ", pc.connectionState);
|
|
2817
2865
|
if (pc.connectionState === "failed") {
|
|
2818
|
-
|
|
2819
|
-
|
|
2866
|
+
if (!resolved) {
|
|
2867
|
+
resolved = true;
|
|
2868
|
+
clearTimeout(timeout);
|
|
2869
|
+
pc.close();
|
|
2870
|
+
reject(new Error("WebRTC Connection failed"));
|
|
2871
|
+
}
|
|
2820
2872
|
} else if (pc.connectionState === "closed") {
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2873
|
+
if (!resolved) {
|
|
2874
|
+
resolved = true;
|
|
2875
|
+
clearTimeout(timeout);
|
|
2876
|
+
reject(new Error("WebRTC Connection closed"));
|
|
2877
|
+
}
|
|
2878
|
+
} else if (pc.connectionState === "connected") {
|
|
2879
|
+
console.log("WebRTC Connection established successfully");
|
|
2824
2880
|
}
|
|
2825
2881
|
},
|
|
2826
2882
|
false,
|
|
2827
2883
|
);
|
|
2828
2884
|
|
|
2885
|
+
// Add ICE connection state change handler for better debugging
|
|
2886
|
+
pc.addEventListener("iceconnectionstatechange", () => {
|
|
2887
|
+
console.log("ICE Connection state: ", pc.iceConnectionState);
|
|
2888
|
+
if (pc.iceConnectionState === "failed") {
|
|
2889
|
+
if (!resolved) {
|
|
2890
|
+
resolved = true;
|
|
2891
|
+
clearTimeout(timeout);
|
|
2892
|
+
pc.close();
|
|
2893
|
+
reject(new Error("ICE Connection failed"));
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
});
|
|
2897
|
+
|
|
2829
2898
|
if (config.on_init) {
|
|
2830
2899
|
await config.on_init(pc);
|
|
2831
2900
|
delete config.on_init;
|
|
2832
2901
|
}
|
|
2902
|
+
|
|
2833
2903
|
let channel = pc.createDataChannel(config.peer_id, { ordered: true });
|
|
2834
2904
|
channel.binaryType = "arraybuffer";
|
|
2905
|
+
|
|
2906
|
+
// Wait for ICE gathering to complete before creating offer
|
|
2835
2907
|
const offer = await pc.createOffer();
|
|
2836
2908
|
await pc.setLocalDescription(offer);
|
|
2909
|
+
|
|
2910
|
+
// Wait for ICE candidates to be gathered (important for Firefox)
|
|
2911
|
+
await new Promise((resolveIce) => {
|
|
2912
|
+
if (pc.iceGatheringState === "complete") {
|
|
2913
|
+
resolveIce();
|
|
2914
|
+
} else {
|
|
2915
|
+
pc.addEventListener("icegatheringstatechange", () => {
|
|
2916
|
+
if (pc.iceGatheringState === "complete") {
|
|
2917
|
+
resolveIce();
|
|
2918
|
+
}
|
|
2919
|
+
});
|
|
2920
|
+
// Don't wait forever for ICE gathering
|
|
2921
|
+
setTimeout(resolveIce, 5000);
|
|
2922
|
+
}
|
|
2923
|
+
});
|
|
2924
|
+
|
|
2837
2925
|
const svc = await server.getService(service_id);
|
|
2838
2926
|
const answer = await svc.offer({
|
|
2839
2927
|
sdp: pc.localDescription.sdp,
|
|
@@ -2843,77 +2931,102 @@ async function getRTCService(server, service_id, config) {
|
|
|
2843
2931
|
channel.onopen = () => {
|
|
2844
2932
|
config.channel = channel;
|
|
2845
2933
|
config.workspace = answer.workspace;
|
|
2846
|
-
//
|
|
2847
|
-
// This is needed for safari to work
|
|
2934
|
+
// Increase timeout for Firefox compatibility
|
|
2848
2935
|
setTimeout(async () => {
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
service_id: {
|
|
2876
|
-
type: "string",
|
|
2877
|
-
description:
|
|
2878
|
-
"Service ID. This should be a service id in the format: 'workspace/service_id', 'workspace/client_id:service_id' or 'workspace/client_id:service_id@app_id'",
|
|
2879
|
-
},
|
|
2880
|
-
config: {
|
|
2936
|
+
if (!resolved) {
|
|
2937
|
+
try {
|
|
2938
|
+
const rpc = await _setupRPC(config);
|
|
2939
|
+
pc.rpc = rpc;
|
|
2940
|
+
async function get_service(name, ...args) {
|
|
2941
|
+
(0,_utils__WEBPACK_IMPORTED_MODULE_1__.assert)(
|
|
2942
|
+
!name.includes(":"),
|
|
2943
|
+
"WebRTC service name should not contain ':'",
|
|
2944
|
+
);
|
|
2945
|
+
(0,_utils__WEBPACK_IMPORTED_MODULE_1__.assert)(
|
|
2946
|
+
!name.includes("/"),
|
|
2947
|
+
"WebRTC service name should not contain '/'",
|
|
2948
|
+
);
|
|
2949
|
+
return await rpc.get_remote_service(
|
|
2950
|
+
config.workspace + "/" + config.peer_id + ":" + name,
|
|
2951
|
+
...args,
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
async function disconnect() {
|
|
2955
|
+
await rpc.disconnect();
|
|
2956
|
+
pc.close();
|
|
2957
|
+
}
|
|
2958
|
+
pc.getService = (0,_utils_schema_js__WEBPACK_IMPORTED_MODULE_2__.schemaFunction)(get_service, {
|
|
2959
|
+
name: "getService",
|
|
2960
|
+
description: "Get a remote service via webrtc",
|
|
2961
|
+
parameters: {
|
|
2881
2962
|
type: "object",
|
|
2882
|
-
|
|
2963
|
+
properties: {
|
|
2964
|
+
service_id: {
|
|
2965
|
+
type: "string",
|
|
2966
|
+
description:
|
|
2967
|
+
"Service ID. This should be a service id in the format: 'workspace/service_id', 'workspace/client_id:service_id' or 'workspace/client_id:service_id@app_id'",
|
|
2968
|
+
},
|
|
2969
|
+
config: {
|
|
2970
|
+
type: "object",
|
|
2971
|
+
description: "Options for the service",
|
|
2972
|
+
},
|
|
2973
|
+
},
|
|
2974
|
+
required: ["id"],
|
|
2883
2975
|
},
|
|
2884
|
-
}
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
name: "registerCodec",
|
|
2895
|
-
description: "Register a codec for the webrtc connection",
|
|
2896
|
-
parameters: {
|
|
2897
|
-
type: "object",
|
|
2898
|
-
properties: {
|
|
2899
|
-
codec: {
|
|
2976
|
+
});
|
|
2977
|
+
pc.disconnect = (0,_utils_schema_js__WEBPACK_IMPORTED_MODULE_2__.schemaFunction)(disconnect, {
|
|
2978
|
+
name: "disconnect",
|
|
2979
|
+
description: "Disconnect from the webrtc connection via webrtc",
|
|
2980
|
+
parameters: { type: "object", properties: {} },
|
|
2981
|
+
});
|
|
2982
|
+
pc.registerCodec = (0,_utils_schema_js__WEBPACK_IMPORTED_MODULE_2__.schemaFunction)(rpc.register_codec, {
|
|
2983
|
+
name: "registerCodec",
|
|
2984
|
+
description: "Register a codec for the webrtc connection",
|
|
2985
|
+
parameters: {
|
|
2900
2986
|
type: "object",
|
|
2901
|
-
description: "Codec to register",
|
|
2902
2987
|
properties: {
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2988
|
+
codec: {
|
|
2989
|
+
type: "object",
|
|
2990
|
+
description: "Codec to register",
|
|
2991
|
+
properties: {
|
|
2992
|
+
name: { type: "string" },
|
|
2993
|
+
type: {},
|
|
2994
|
+
encoder: { type: "function" },
|
|
2995
|
+
decoder: { type: "function" },
|
|
2996
|
+
},
|
|
2997
|
+
},
|
|
2907
2998
|
},
|
|
2908
2999
|
},
|
|
2909
|
-
}
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
3000
|
+
});
|
|
3001
|
+
resolved = true;
|
|
3002
|
+
clearTimeout(timeout);
|
|
3003
|
+
resolve(pc);
|
|
3004
|
+
} catch (e) {
|
|
3005
|
+
if (!resolved) {
|
|
3006
|
+
resolved = true;
|
|
3007
|
+
clearTimeout(timeout);
|
|
3008
|
+
reject(e);
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
}, 1000); // Increase timeout to 1 second for Firefox
|
|
3013
|
+
};
|
|
3014
|
+
|
|
3015
|
+
channel.onclose = () => {
|
|
3016
|
+
if (!resolved) {
|
|
3017
|
+
resolved = true;
|
|
3018
|
+
clearTimeout(timeout);
|
|
3019
|
+
reject(new Error("Data channel closed"));
|
|
3020
|
+
}
|
|
2914
3021
|
};
|
|
2915
3022
|
|
|
2916
|
-
channel.
|
|
3023
|
+
channel.onerror = (error) => {
|
|
3024
|
+
if (!resolved) {
|
|
3025
|
+
resolved = true;
|
|
3026
|
+
clearTimeout(timeout);
|
|
3027
|
+
reject(new Error(`Data channel error: ${error}`));
|
|
3028
|
+
}
|
|
3029
|
+
};
|
|
2917
3030
|
|
|
2918
3031
|
await pc.setRemoteDescription(
|
|
2919
3032
|
new RTCSessionDescription({
|
|
@@ -2922,7 +3035,11 @@ async function getRTCService(server, service_id, config) {
|
|
|
2922
3035
|
}),
|
|
2923
3036
|
);
|
|
2924
3037
|
} catch (e) {
|
|
2925
|
-
|
|
3038
|
+
if (!resolved) {
|
|
3039
|
+
resolved = true;
|
|
3040
|
+
clearTimeout(timeout);
|
|
3041
|
+
reject(e);
|
|
3042
|
+
}
|
|
2926
3043
|
}
|
|
2927
3044
|
});
|
|
2928
3045
|
}
|