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.
@@ -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
- (0,_utils__WEBPACK_IMPORTED_MODULE_0__.assert)(
964
- store,
965
- `Failed to create session store ${session_id} due to invalid parent`,
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
- await Promise.all(tasks);
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
- let clear_after_called = true;
1185
- for (let arg of args) {
1186
- if (typeof arg === "object" && arg._rintf === true) {
1187
- clear_after_called = false;
1188
- break;
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
- return null;
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
- try {
1865
- while (true) {
1866
- try {
1867
- const next_item = await gen_method();
1868
- // Check for StopIteration signal
1869
- if (next_item && next_item._rtype === "stop_iteration") {
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
- pc.close();
2819
- reject(new Error("WebRTC Connection failed"));
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
- reject(new Error("WebRTC Connection closed"));
2822
- } else {
2823
- console.log("WebRTC Connection state: ", pc.connectionState);
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
- // Wait for the channel to be open before returning the rpc
2847
- // This is needed for safari to work
2934
+ // Increase timeout for Firefox compatibility
2848
2935
  setTimeout(async () => {
2849
- const rpc = await _setupRPC(config);
2850
- pc.rpc = rpc;
2851
- async function get_service(name, ...args) {
2852
- (0,_utils__WEBPACK_IMPORTED_MODULE_1__.assert)(
2853
- !name.includes(":"),
2854
- "WebRTC service name should not contain ':'",
2855
- );
2856
- (0,_utils__WEBPACK_IMPORTED_MODULE_1__.assert)(
2857
- !name.includes("/"),
2858
- "WebRTC service name should not contain '/'",
2859
- );
2860
- return await rpc.get_remote_service(
2861
- config.workspace + "/" + config.peer_id + ":" + name,
2862
- ...args,
2863
- );
2864
- }
2865
- async function disconnect() {
2866
- await rpc.disconnect();
2867
- pc.close();
2868
- }
2869
- pc.getService = (0,_utils_schema_js__WEBPACK_IMPORTED_MODULE_2__.schemaFunction)(get_service, {
2870
- name: "getService",
2871
- description: "Get a remote service via webrtc",
2872
- parameters: {
2873
- type: "object",
2874
- properties: {
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
- description: "Options for the service",
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
- required: ["id"],
2886
- },
2887
- });
2888
- pc.disconnect = (0,_utils_schema_js__WEBPACK_IMPORTED_MODULE_2__.schemaFunction)(disconnect, {
2889
- name: "disconnect",
2890
- description: "Disconnect from the webrtc connection via webrtc",
2891
- parameters: { type: "object", properties: {} },
2892
- });
2893
- pc.registerCodec = (0,_utils_schema_js__WEBPACK_IMPORTED_MODULE_2__.schemaFunction)(rpc.register_codec, {
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
- name: { type: "string" },
2904
- type: {},
2905
- encoder: { type: "function" },
2906
- decoder: { type: "function" },
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
- resolve(pc);
2913
- }, 500);
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.onclose = () => reject(new Error("Data channel closed"));
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
- reject(e);
3038
+ if (!resolved) {
3039
+ resolved = true;
3040
+ clearTimeout(timeout);
3041
+ reject(e);
3042
+ }
2926
3043
  }
2927
3044
  });
2928
3045
  }