@gethashd/bytecave-browser 1.0.47 → 1.0.48

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.
@@ -6226,6 +6226,101 @@ var StorageWebSocketClient = class {
6226
6226
  }
6227
6227
  };
6228
6228
 
6229
+ // src/storage-webtransport.ts
6230
+ var StorageWebTransportClient = class {
6231
+ constructor(nodeMultiaddr) {
6232
+ this.nodeMultiaddr = nodeMultiaddr;
6233
+ }
6234
+ /**
6235
+ * Store data via WebTransport direct to node
6236
+ */
6237
+ async store(request) {
6238
+ try {
6239
+ if (typeof WebTransport === "undefined") {
6240
+ return {
6241
+ success: false,
6242
+ error: "WebTransport not supported in this browser"
6243
+ };
6244
+ }
6245
+ const url = this.multiaddrToWebTransportUrl(this.nodeMultiaddr);
6246
+ if (!url) {
6247
+ return {
6248
+ success: false,
6249
+ error: "Invalid WebTransport multiaddr"
6250
+ };
6251
+ }
6252
+ console.log("[WebTransport] Connecting to node:", url);
6253
+ const transport = new WebTransport(url);
6254
+ await transport.ready;
6255
+ console.log("[WebTransport] Connected, opening bidirectional stream");
6256
+ const stream = await transport.createBidirectionalStream();
6257
+ const writer = stream.writable.getWriter();
6258
+ const reader = stream.readable.getReader();
6259
+ const requestData = {
6260
+ type: "storage-request",
6261
+ data: Array.from(request.data),
6262
+ // Convert Uint8Array to array for JSON
6263
+ contentType: request.contentType,
6264
+ hashIdToken: request.hashIdToken,
6265
+ authorization: request.authorization
6266
+ };
6267
+ const requestJson = JSON.stringify(requestData);
6268
+ const requestBytes = new TextEncoder().encode(requestJson);
6269
+ await writer.write(requestBytes);
6270
+ await writer.close();
6271
+ console.log("[WebTransport] Request sent, waiting for response");
6272
+ const { value, done } = await reader.read();
6273
+ if (done || !value) {
6274
+ return {
6275
+ success: false,
6276
+ error: "No response from node"
6277
+ };
6278
+ }
6279
+ const responseJson = new TextDecoder().decode(value);
6280
+ const response = JSON.parse(responseJson);
6281
+ console.log("[WebTransport] Response received:", response);
6282
+ await transport.close();
6283
+ return response;
6284
+ } catch (error) {
6285
+ console.error("[WebTransport] Storage failed:", error);
6286
+ return {
6287
+ success: false,
6288
+ error: error.message || "WebTransport storage failed"
6289
+ };
6290
+ }
6291
+ }
6292
+ /**
6293
+ * Convert libp2p multiaddr to WebTransport URL
6294
+ */
6295
+ multiaddrToWebTransportUrl(multiaddr2) {
6296
+ try {
6297
+ const parts = multiaddr2.split("/").filter((p) => p);
6298
+ let ip = "";
6299
+ let port = "";
6300
+ let hasQuic = false;
6301
+ let hasWebTransport = false;
6302
+ for (let i = 0; i < parts.length; i++) {
6303
+ if (parts[i] === "ip4" && i + 1 < parts.length) {
6304
+ ip = parts[i + 1];
6305
+ } else if (parts[i] === "udp" && i + 1 < parts.length) {
6306
+ port = parts[i + 1];
6307
+ } else if (parts[i] === "quic-v1") {
6308
+ hasQuic = true;
6309
+ } else if (parts[i] === "webtransport") {
6310
+ hasWebTransport = true;
6311
+ }
6312
+ }
6313
+ if (!ip || !port || !hasQuic || !hasWebTransport) {
6314
+ return null;
6315
+ }
6316
+ return `https://${ip}:${port}`;
6317
+ } catch (error) {
6318
+ console.error("[WebTransport] Failed to parse multiaddr:", error);
6319
+ return null;
6320
+ }
6321
+ }
6322
+ };
6323
+
6229
6324
  // src/contracts/ContentRegistry.ts
6230
6325
  var CONTENT_REGISTRY_ABI = [
6231
6326
  "function registerContent(bytes32 cid, address owner, bytes32 appId, uint256 hashIdToken) external",
@@ -6591,15 +6686,76 @@ Nonce: ${nonce}`;
6591
6686
  console.warn("[ByteCave] Failed to create authorization:", err.message);
6592
6687
  }
6593
6688
  }
6594
- if (!this.config.relayWsUrl) {
6595
- return { success: false, error: "WebSocket relay URL not configured" };
6689
+ if (this.config.relayWsUrl) {
6690
+ console.log("[ByteCave] Attempting storage via WebSocket relay");
6691
+ try {
6692
+ if (!this.storageWsClient) {
6693
+ this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6694
+ }
6695
+ const wsAuth = authorization ? {
6696
+ signature: authorization.signature,
6697
+ address: authorization.sender,
6698
+ timestamp: authorization.timestamp,
6699
+ nonce: authorization.nonce,
6700
+ appId: authorization.appId,
6701
+ contentHash: authorization.contentHash
6702
+ } : void 0;
6703
+ const result = await this.storageWsClient.store({
6704
+ data: dataArray,
6705
+ contentType: mimeType || "application/octet-stream",
6706
+ hashIdToken,
6707
+ authorization: wsAuth,
6708
+ timeout: 3e4
6709
+ });
6710
+ if (result.success && result.cid) {
6711
+ console.log("[ByteCave] \u2713 WebSocket storage successful:", result.cid);
6712
+ return {
6713
+ success: true,
6714
+ cid: result.cid,
6715
+ peerId: "relay-ws"
6716
+ };
6717
+ }
6718
+ console.warn("[ByteCave] WebSocket storage failed, trying WebTransport:", result.error);
6719
+ } catch (err) {
6720
+ console.warn("[ByteCave] WebSocket storage exception, trying WebTransport:", err.message);
6721
+ }
6722
+ } else {
6723
+ console.log("[ByteCave] WebSocket relay not configured, trying WebTransport");
6596
6724
  }
6597
- console.log("[ByteCave] Storing via WebSocket relay");
6725
+ console.log("[ByteCave] Attempting storage via WebTransport direct");
6598
6726
  try {
6599
- if (!this.storageWsClient) {
6600
- this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6727
+ let nodes = [];
6728
+ if (this.relayDiscovery) {
6729
+ nodes = await this.relayDiscovery.getConnectedPeers();
6730
+ } else if (this.contractDiscovery) {
6731
+ nodes = await this.contractDiscovery.getActiveNodes();
6732
+ }
6733
+ if (nodes.length === 0) {
6734
+ return {
6735
+ success: false,
6736
+ error: "No nodes available for WebTransport storage"
6737
+ };
6738
+ }
6739
+ const nodeWithWebTransport = nodes.find(
6740
+ (node) => node.multiaddrs?.some((addr) => addr.includes("/webtransport"))
6741
+ );
6742
+ if (!nodeWithWebTransport || !nodeWithWebTransport.multiaddrs) {
6743
+ return {
6744
+ success: false,
6745
+ error: "No nodes with WebTransport support found"
6746
+ };
6601
6747
  }
6602
- const wsAuth = authorization ? {
6748
+ const wtMultiaddr = nodeWithWebTransport.multiaddrs.find(
6749
+ (addr) => addr.includes("/webtransport")
6750
+ );
6751
+ if (!wtMultiaddr) {
6752
+ return {
6753
+ success: false,
6754
+ error: "No WebTransport multiaddr found"
6755
+ };
6756
+ }
6757
+ const wtClient = new StorageWebTransportClient(wtMultiaddr);
6758
+ const wtAuth = authorization ? {
6603
6759
  signature: authorization.signature,
6604
6760
  address: authorization.sender,
6605
6761
  timestamp: authorization.timestamp,
@@ -6607,26 +6763,25 @@ Nonce: ${nonce}`;
6607
6763
  appId: authorization.appId,
6608
6764
  contentHash: authorization.contentHash
6609
6765
  } : void 0;
6610
- const result = await this.storageWsClient.store({
6766
+ const result = await wtClient.store({
6611
6767
  data: dataArray,
6612
6768
  contentType: mimeType || "application/octet-stream",
6613
6769
  hashIdToken,
6614
- authorization: wsAuth,
6615
- timeout: 3e4
6770
+ authorization: wtAuth
6616
6771
  });
6617
6772
  if (result.success && result.cid) {
6618
- console.log("[ByteCave] \u2713 WebSocket storage successful:", result.cid);
6773
+ console.log("[ByteCave] \u2713 WebTransport storage successful:", result.cid);
6619
6774
  return {
6620
6775
  success: true,
6621
6776
  cid: result.cid,
6622
- peerId: "relay-ws"
6777
+ peerId: nodeWithWebTransport.peerId
6623
6778
  };
6624
6779
  }
6625
- console.warn("[ByteCave] WebSocket storage failed:", result.error);
6626
- return { success: false, error: result.error || "WebSocket storage failed" };
6780
+ console.error("[ByteCave] WebTransport storage failed:", result.error);
6781
+ return { success: false, error: result.error || "All storage methods failed" };
6627
6782
  } catch (err) {
6628
- console.error("[ByteCave] WebSocket storage exception:", err.message);
6629
- return { success: false, error: err.message };
6783
+ console.error("[ByteCave] WebTransport storage exception:", err.message);
6784
+ return { success: false, error: `All storage methods failed: ${err.message}` };
6630
6785
  }
6631
6786
  }
6632
6787
  /**
package/dist/index.cjs CHANGED
@@ -6279,6 +6279,101 @@ var StorageWebSocketClient = class {
6279
6279
  }
6280
6280
  };
6281
6281
 
6282
+ // src/storage-webtransport.ts
6283
+ var StorageWebTransportClient = class {
6284
+ constructor(nodeMultiaddr) {
6285
+ this.nodeMultiaddr = nodeMultiaddr;
6286
+ }
6287
+ /**
6288
+ * Store data via WebTransport direct to node
6289
+ */
6290
+ async store(request) {
6291
+ try {
6292
+ if (typeof WebTransport === "undefined") {
6293
+ return {
6294
+ success: false,
6295
+ error: "WebTransport not supported in this browser"
6296
+ };
6297
+ }
6298
+ const url = this.multiaddrToWebTransportUrl(this.nodeMultiaddr);
6299
+ if (!url) {
6300
+ return {
6301
+ success: false,
6302
+ error: "Invalid WebTransport multiaddr"
6303
+ };
6304
+ }
6305
+ console.log("[WebTransport] Connecting to node:", url);
6306
+ const transport = new WebTransport(url);
6307
+ await transport.ready;
6308
+ console.log("[WebTransport] Connected, opening bidirectional stream");
6309
+ const stream = await transport.createBidirectionalStream();
6310
+ const writer = stream.writable.getWriter();
6311
+ const reader = stream.readable.getReader();
6312
+ const requestData = {
6313
+ type: "storage-request",
6314
+ data: Array.from(request.data),
6315
+ // Convert Uint8Array to array for JSON
6316
+ contentType: request.contentType,
6317
+ hashIdToken: request.hashIdToken,
6318
+ authorization: request.authorization
6319
+ };
6320
+ const requestJson = JSON.stringify(requestData);
6321
+ const requestBytes = new TextEncoder().encode(requestJson);
6322
+ await writer.write(requestBytes);
6323
+ await writer.close();
6324
+ console.log("[WebTransport] Request sent, waiting for response");
6325
+ const { value, done } = await reader.read();
6326
+ if (done || !value) {
6327
+ return {
6328
+ success: false,
6329
+ error: "No response from node"
6330
+ };
6331
+ }
6332
+ const responseJson = new TextDecoder().decode(value);
6333
+ const response = JSON.parse(responseJson);
6334
+ console.log("[WebTransport] Response received:", response);
6335
+ await transport.close();
6336
+ return response;
6337
+ } catch (error) {
6338
+ console.error("[WebTransport] Storage failed:", error);
6339
+ return {
6340
+ success: false,
6341
+ error: error.message || "WebTransport storage failed"
6342
+ };
6343
+ }
6344
+ }
6345
+ /**
6346
+ * Convert libp2p multiaddr to WebTransport URL
6347
+ */
6348
+ multiaddrToWebTransportUrl(multiaddr2) {
6349
+ try {
6350
+ const parts = multiaddr2.split("/").filter((p) => p);
6351
+ let ip = "";
6352
+ let port = "";
6353
+ let hasQuic = false;
6354
+ let hasWebTransport = false;
6355
+ for (let i = 0; i < parts.length; i++) {
6356
+ if (parts[i] === "ip4" && i + 1 < parts.length) {
6357
+ ip = parts[i + 1];
6358
+ } else if (parts[i] === "udp" && i + 1 < parts.length) {
6359
+ port = parts[i + 1];
6360
+ } else if (parts[i] === "quic-v1") {
6361
+ hasQuic = true;
6362
+ } else if (parts[i] === "webtransport") {
6363
+ hasWebTransport = true;
6364
+ }
6365
+ }
6366
+ if (!ip || !port || !hasQuic || !hasWebTransport) {
6367
+ return null;
6368
+ }
6369
+ return `https://${ip}:${port}`;
6370
+ } catch (error) {
6371
+ console.error("[WebTransport] Failed to parse multiaddr:", error);
6372
+ return null;
6373
+ }
6374
+ }
6375
+ };
6376
+
6282
6377
  // src/contracts/ContentRegistry.ts
6283
6378
  var CONTENT_REGISTRY_ABI = [
6284
6379
  "function registerContent(bytes32 cid, address owner, bytes32 appId, uint256 hashIdToken) external",
@@ -6644,15 +6739,76 @@ Nonce: ${nonce}`;
6644
6739
  console.warn("[ByteCave] Failed to create authorization:", err.message);
6645
6740
  }
6646
6741
  }
6647
- if (!this.config.relayWsUrl) {
6648
- return { success: false, error: "WebSocket relay URL not configured" };
6742
+ if (this.config.relayWsUrl) {
6743
+ console.log("[ByteCave] Attempting storage via WebSocket relay");
6744
+ try {
6745
+ if (!this.storageWsClient) {
6746
+ this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6747
+ }
6748
+ const wsAuth = authorization ? {
6749
+ signature: authorization.signature,
6750
+ address: authorization.sender,
6751
+ timestamp: authorization.timestamp,
6752
+ nonce: authorization.nonce,
6753
+ appId: authorization.appId,
6754
+ contentHash: authorization.contentHash
6755
+ } : void 0;
6756
+ const result = await this.storageWsClient.store({
6757
+ data: dataArray,
6758
+ contentType: mimeType || "application/octet-stream",
6759
+ hashIdToken,
6760
+ authorization: wsAuth,
6761
+ timeout: 3e4
6762
+ });
6763
+ if (result.success && result.cid) {
6764
+ console.log("[ByteCave] \u2713 WebSocket storage successful:", result.cid);
6765
+ return {
6766
+ success: true,
6767
+ cid: result.cid,
6768
+ peerId: "relay-ws"
6769
+ };
6770
+ }
6771
+ console.warn("[ByteCave] WebSocket storage failed, trying WebTransport:", result.error);
6772
+ } catch (err) {
6773
+ console.warn("[ByteCave] WebSocket storage exception, trying WebTransport:", err.message);
6774
+ }
6775
+ } else {
6776
+ console.log("[ByteCave] WebSocket relay not configured, trying WebTransport");
6649
6777
  }
6650
- console.log("[ByteCave] Storing via WebSocket relay");
6778
+ console.log("[ByteCave] Attempting storage via WebTransport direct");
6651
6779
  try {
6652
- if (!this.storageWsClient) {
6653
- this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6780
+ let nodes = [];
6781
+ if (this.relayDiscovery) {
6782
+ nodes = await this.relayDiscovery.getConnectedPeers();
6783
+ } else if (this.contractDiscovery) {
6784
+ nodes = await this.contractDiscovery.getActiveNodes();
6785
+ }
6786
+ if (nodes.length === 0) {
6787
+ return {
6788
+ success: false,
6789
+ error: "No nodes available for WebTransport storage"
6790
+ };
6791
+ }
6792
+ const nodeWithWebTransport = nodes.find(
6793
+ (node) => node.multiaddrs?.some((addr) => addr.includes("/webtransport"))
6794
+ );
6795
+ if (!nodeWithWebTransport || !nodeWithWebTransport.multiaddrs) {
6796
+ return {
6797
+ success: false,
6798
+ error: "No nodes with WebTransport support found"
6799
+ };
6654
6800
  }
6655
- const wsAuth = authorization ? {
6801
+ const wtMultiaddr = nodeWithWebTransport.multiaddrs.find(
6802
+ (addr) => addr.includes("/webtransport")
6803
+ );
6804
+ if (!wtMultiaddr) {
6805
+ return {
6806
+ success: false,
6807
+ error: "No WebTransport multiaddr found"
6808
+ };
6809
+ }
6810
+ const wtClient = new StorageWebTransportClient(wtMultiaddr);
6811
+ const wtAuth = authorization ? {
6656
6812
  signature: authorization.signature,
6657
6813
  address: authorization.sender,
6658
6814
  timestamp: authorization.timestamp,
@@ -6660,26 +6816,25 @@ Nonce: ${nonce}`;
6660
6816
  appId: authorization.appId,
6661
6817
  contentHash: authorization.contentHash
6662
6818
  } : void 0;
6663
- const result = await this.storageWsClient.store({
6819
+ const result = await wtClient.store({
6664
6820
  data: dataArray,
6665
6821
  contentType: mimeType || "application/octet-stream",
6666
6822
  hashIdToken,
6667
- authorization: wsAuth,
6668
- timeout: 3e4
6823
+ authorization: wtAuth
6669
6824
  });
6670
6825
  if (result.success && result.cid) {
6671
- console.log("[ByteCave] \u2713 WebSocket storage successful:", result.cid);
6826
+ console.log("[ByteCave] \u2713 WebTransport storage successful:", result.cid);
6672
6827
  return {
6673
6828
  success: true,
6674
6829
  cid: result.cid,
6675
- peerId: "relay-ws"
6830
+ peerId: nodeWithWebTransport.peerId
6676
6831
  };
6677
6832
  }
6678
- console.warn("[ByteCave] WebSocket storage failed:", result.error);
6679
- return { success: false, error: result.error || "WebSocket storage failed" };
6833
+ console.error("[ByteCave] WebTransport storage failed:", result.error);
6834
+ return { success: false, error: result.error || "All storage methods failed" };
6680
6835
  } catch (err) {
6681
- console.error("[ByteCave] WebSocket storage exception:", err.message);
6682
- return { success: false, error: err.message };
6836
+ console.error("[ByteCave] WebTransport storage exception:", err.message);
6837
+ return { success: false, error: `All storage methods failed: ${err.message}` };
6683
6838
  }
6684
6839
  }
6685
6840
  /**
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  useHashdImage,
14
14
  useHashdMedia,
15
15
  useHashdUrl
16
- } from "./chunk-FOQ3CE3H.js";
16
+ } from "./chunk-FSNCYF3O.js";
17
17
  import {
18
18
  clearHashdCache,
19
19
  createHashdUrl,
@@ -8,7 +8,7 @@ import {
8
8
  useHashdImage,
9
9
  useHashdMedia,
10
10
  useHashdUrl
11
- } from "../chunk-FOQ3CE3H.js";
11
+ } from "../chunk-FSNCYF3O.js";
12
12
  import "../chunk-EEZWRIUI.js";
13
13
  export {
14
14
  HashdAudio,
@@ -0,0 +1,36 @@
1
+ /**
2
+ * WebTransport Storage Client
3
+ *
4
+ * Provides direct browser-to-node storage over WebTransport (HTTP/3)
5
+ * Used as fallback when WebSocket relay is unavailable
6
+ */
7
+ export interface WebTransportStorageRequest {
8
+ data: Uint8Array;
9
+ contentType: string;
10
+ hashIdToken?: number;
11
+ authorization?: {
12
+ signature: string;
13
+ address: string;
14
+ timestamp: number;
15
+ nonce: string;
16
+ appId: string;
17
+ contentHash: string;
18
+ };
19
+ }
20
+ export interface WebTransportStorageResponse {
21
+ success: boolean;
22
+ cid?: string;
23
+ error?: string;
24
+ }
25
+ export declare class StorageWebTransportClient {
26
+ private nodeMultiaddr;
27
+ constructor(nodeMultiaddr: string);
28
+ /**
29
+ * Store data via WebTransport direct to node
30
+ */
31
+ store(request: WebTransportStorageRequest): Promise<WebTransportStorageResponse>;
32
+ /**
33
+ * Convert libp2p multiaddr to WebTransport URL
34
+ */
35
+ private multiaddrToWebTransportUrl;
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethashd/bytecave-browser",
3
- "version": "1.0.47",
3
+ "version": "1.0.48",
4
4
  "description": "ByteCave browser client for WebRTC P2P connections to storage nodes",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -45,6 +45,7 @@
45
45
  "@libp2p/identify": "^4.0.9",
46
46
  "@libp2p/webrtc": "^6.0.10",
47
47
  "@libp2p/websockets": "^10.1.2",
48
+ "@libp2p/webtransport": "^6.0.12",
48
49
  "@multiformats/multiaddr": "^13.0.1",
49
50
  "ethers": "^6.0.0",
50
51
  "libp2p": "^3.1.2",
package/src/client.ts CHANGED
@@ -21,6 +21,7 @@ import { ethers } from 'ethers';
21
21
  import { ContractDiscovery, RelayDiscovery } from './discovery.js';
22
22
  import { p2pProtocolClient } from './p2p-protocols.js';
23
23
  import { StorageWebSocketClient } from './storage-websocket.js';
24
+ import { StorageWebTransportClient } from './storage-webtransport.js';
24
25
  import { CONTENT_REGISTRY_ABI } from './contracts/ContentRegistry.js';
25
26
  import type {
26
27
  ByteCaveConfig,
@@ -476,19 +477,98 @@ Nonce: ${nonce}`;
476
477
  }
477
478
  }
478
479
 
479
- // Use WebSocket relay for storage (simpler connection management than direct P2P)
480
- if (!this.config.relayWsUrl) {
481
- return { success: false, error: 'WebSocket relay URL not configured' };
480
+ // Storage fallback strategy:
481
+ // 1. Try WebSocket relay (most reliable)
482
+ // 2. If relay fails, try WebTransport direct to node
483
+
484
+ // Try WebSocket relay first
485
+ if (this.config.relayWsUrl) {
486
+ console.log('[ByteCave] Attempting storage via WebSocket relay');
487
+
488
+ try {
489
+ if (!this.storageWsClient) {
490
+ this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
491
+ }
492
+
493
+ const wsAuth = authorization ? {
494
+ signature: authorization.signature,
495
+ address: authorization.sender,
496
+ timestamp: authorization.timestamp,
497
+ nonce: authorization.nonce,
498
+ appId: authorization.appId,
499
+ contentHash: authorization.contentHash
500
+ } : undefined;
501
+
502
+ const result = await this.storageWsClient.store({
503
+ data: dataArray,
504
+ contentType: mimeType || 'application/octet-stream',
505
+ hashIdToken,
506
+ authorization: wsAuth,
507
+ timeout: 30000
508
+ });
509
+
510
+ if (result.success && result.cid) {
511
+ console.log('[ByteCave] ✓ WebSocket storage successful:', result.cid);
512
+ return {
513
+ success: true,
514
+ cid: result.cid,
515
+ peerId: 'relay-ws'
516
+ };
517
+ }
518
+
519
+ console.warn('[ByteCave] WebSocket storage failed, trying WebTransport:', result.error);
520
+ } catch (err: any) {
521
+ console.warn('[ByteCave] WebSocket storage exception, trying WebTransport:', err.message);
522
+ }
523
+ } else {
524
+ console.log('[ByteCave] WebSocket relay not configured, trying WebTransport');
482
525
  }
483
526
 
484
- console.log('[ByteCave] Storing via WebSocket relay');
527
+ // Fallback to WebTransport direct to node
528
+ console.log('[ByteCave] Attempting storage via WebTransport direct');
485
529
 
486
530
  try {
487
- if (!this.storageWsClient) {
488
- this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
531
+ // Get nodes from relay discovery or contract discovery
532
+ let nodes: any[] = [];
533
+
534
+ if (this.relayDiscovery) {
535
+ nodes = await this.relayDiscovery.getConnectedPeers();
536
+ } else if (this.contractDiscovery) {
537
+ nodes = await this.contractDiscovery.getActiveNodes();
489
538
  }
490
539
 
491
- const wsAuth = authorization ? {
540
+ if (nodes.length === 0) {
541
+ return {
542
+ success: false,
543
+ error: 'No nodes available for WebTransport storage'
544
+ };
545
+ }
546
+
547
+ const nodeWithWebTransport = nodes.find((node: any) =>
548
+ node.multiaddrs?.some((addr: string) => addr.includes('/webtransport'))
549
+ );
550
+
551
+ if (!nodeWithWebTransport || !nodeWithWebTransport.multiaddrs) {
552
+ return {
553
+ success: false,
554
+ error: 'No nodes with WebTransport support found'
555
+ };
556
+ }
557
+
558
+ const wtMultiaddr = nodeWithWebTransport.multiaddrs.find((addr: string) =>
559
+ addr.includes('/webtransport')
560
+ );
561
+
562
+ if (!wtMultiaddr) {
563
+ return {
564
+ success: false,
565
+ error: 'No WebTransport multiaddr found'
566
+ };
567
+ }
568
+
569
+ const wtClient = new StorageWebTransportClient(wtMultiaddr);
570
+
571
+ const wtAuth = authorization ? {
492
572
  signature: authorization.signature,
493
573
  address: authorization.sender,
494
574
  timestamp: authorization.timestamp,
@@ -497,28 +577,27 @@ Nonce: ${nonce}`;
497
577
  contentHash: authorization.contentHash
498
578
  } : undefined;
499
579
 
500
- const result = await this.storageWsClient.store({
580
+ const result = await wtClient.store({
501
581
  data: dataArray,
502
582
  contentType: mimeType || 'application/octet-stream',
503
583
  hashIdToken,
504
- authorization: wsAuth,
505
- timeout: 30000
584
+ authorization: wtAuth
506
585
  });
507
586
 
508
587
  if (result.success && result.cid) {
509
- console.log('[ByteCave] ✓ WebSocket storage successful:', result.cid);
588
+ console.log('[ByteCave] ✓ WebTransport storage successful:', result.cid);
510
589
  return {
511
590
  success: true,
512
591
  cid: result.cid,
513
- peerId: 'relay-ws'
592
+ peerId: nodeWithWebTransport.peerId
514
593
  };
515
594
  }
516
595
 
517
- console.warn('[ByteCave] WebSocket storage failed:', result.error);
518
- return { success: false, error: result.error || 'WebSocket storage failed' };
596
+ console.error('[ByteCave] WebTransport storage failed:', result.error);
597
+ return { success: false, error: result.error || 'All storage methods failed' };
519
598
  } catch (err: any) {
520
- console.error('[ByteCave] WebSocket storage exception:', err.message);
521
- return { success: false, error: err.message };
599
+ console.error('[ByteCave] WebTransport storage exception:', err.message);
600
+ return { success: false, error: `All storage methods failed: ${err.message}` };
522
601
  }
523
602
  }
524
603
 
@@ -0,0 +1,156 @@
1
+ /**
2
+ * WebTransport Storage Client
3
+ *
4
+ * Provides direct browser-to-node storage over WebTransport (HTTP/3)
5
+ * Used as fallback when WebSocket relay is unavailable
6
+ */
7
+
8
+ // Logger removed - using console.log for browser compatibility
9
+
10
+ export interface WebTransportStorageRequest {
11
+ data: Uint8Array;
12
+ contentType: string;
13
+ hashIdToken?: number;
14
+ authorization?: {
15
+ signature: string;
16
+ address: string;
17
+ timestamp: number;
18
+ nonce: string;
19
+ appId: string;
20
+ contentHash: string;
21
+ };
22
+ }
23
+
24
+ export interface WebTransportStorageResponse {
25
+ success: boolean;
26
+ cid?: string;
27
+ error?: string;
28
+ }
29
+
30
+ export class StorageWebTransportClient {
31
+ private nodeMultiaddr: string;
32
+
33
+ constructor(nodeMultiaddr: string) {
34
+ this.nodeMultiaddr = nodeMultiaddr;
35
+ }
36
+
37
+ /**
38
+ * Store data via WebTransport direct to node
39
+ */
40
+ async store(request: WebTransportStorageRequest): Promise<WebTransportStorageResponse> {
41
+ try {
42
+ // Check if WebTransport is supported
43
+ if (typeof WebTransport === 'undefined') {
44
+ return {
45
+ success: false,
46
+ error: 'WebTransport not supported in this browser'
47
+ };
48
+ }
49
+
50
+ // Parse multiaddr to get WebTransport URL
51
+ // Format: /ip4/127.0.0.1/udp/4001/quic-v1/webtransport/certhash/...
52
+ const url = this.multiaddrToWebTransportUrl(this.nodeMultiaddr);
53
+ if (!url) {
54
+ return {
55
+ success: false,
56
+ error: 'Invalid WebTransport multiaddr'
57
+ };
58
+ }
59
+
60
+ console.log('[WebTransport] Connecting to node:', url);
61
+
62
+ // Create WebTransport connection
63
+ const transport = new WebTransport(url);
64
+ await transport.ready;
65
+
66
+ console.log('[WebTransport] Connected, opening bidirectional stream');
67
+
68
+ // Open bidirectional stream for storage protocol
69
+ const stream = await transport.createBidirectionalStream();
70
+ const writer = stream.writable.getWriter();
71
+ const reader = stream.readable.getReader();
72
+
73
+ // Send storage request
74
+ const requestData = {
75
+ type: 'storage-request',
76
+ data: Array.from(request.data), // Convert Uint8Array to array for JSON
77
+ contentType: request.contentType,
78
+ hashIdToken: request.hashIdToken,
79
+ authorization: request.authorization
80
+ };
81
+
82
+ const requestJson = JSON.stringify(requestData);
83
+ const requestBytes = new TextEncoder().encode(requestJson);
84
+
85
+ await writer.write(requestBytes);
86
+ await writer.close();
87
+
88
+ console.log('[WebTransport] Request sent, waiting for response');
89
+
90
+ // Read response
91
+ const { value, done } = await reader.read();
92
+ if (done || !value) {
93
+ return {
94
+ success: false,
95
+ error: 'No response from node'
96
+ };
97
+ }
98
+
99
+ const responseJson = new TextDecoder().decode(value);
100
+ const response = JSON.parse(responseJson);
101
+
102
+ console.log('[WebTransport] Response received:', response);
103
+
104
+ // Close transport
105
+ await transport.close();
106
+
107
+ return response;
108
+
109
+ } catch (error: any) {
110
+ console.error('[WebTransport] Storage failed:', error);
111
+ return {
112
+ success: false,
113
+ error: error.message || 'WebTransport storage failed'
114
+ };
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Convert libp2p multiaddr to WebTransport URL
120
+ */
121
+ private multiaddrToWebTransportUrl(multiaddr: string): string | null {
122
+ try {
123
+ // Parse multiaddr format: /ip4/127.0.0.1/udp/4001/quic-v1/webtransport
124
+ const parts = multiaddr.split('/').filter(p => p);
125
+
126
+ let ip = '';
127
+ let port = '';
128
+ let hasQuic = false;
129
+ let hasWebTransport = false;
130
+
131
+ for (let i = 0; i < parts.length; i++) {
132
+ if (parts[i] === 'ip4' && i + 1 < parts.length) {
133
+ ip = parts[i + 1];
134
+ } else if (parts[i] === 'udp' && i + 1 < parts.length) {
135
+ port = parts[i + 1];
136
+ } else if (parts[i] === 'quic-v1') {
137
+ hasQuic = true;
138
+ } else if (parts[i] === 'webtransport') {
139
+ hasWebTransport = true;
140
+ }
141
+ }
142
+
143
+ if (!ip || !port || !hasQuic || !hasWebTransport) {
144
+ return null;
145
+ }
146
+
147
+ // WebTransport URL format: https://ip:port
148
+ // Note: In production, this needs proper certificate handling
149
+ return `https://${ip}:${port}`;
150
+
151
+ } catch (error) {
152
+ console.error('[WebTransport] Failed to parse multiaddr:', error);
153
+ return null;
154
+ }
155
+ }
156
+ }