@gethashd/bytecave-browser 1.0.39 → 1.0.41

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.
@@ -6120,6 +6120,22 @@ var StorageWebSocketClient = class {
6120
6120
  pending.resolve({ success: false, error: message2.error || "Storage failed" });
6121
6121
  }
6122
6122
  }
6123
+ } else if (message2.type === "retrieve-response") {
6124
+ const pending = this.pendingRequests.get(message2.requestId);
6125
+ if (pending) {
6126
+ clearTimeout(pending.timeout);
6127
+ this.pendingRequests.delete(message2.requestId);
6128
+ if (message2.success && message2.data) {
6129
+ const binaryString = atob(message2.data);
6130
+ const bytes = new Uint8Array(binaryString.length);
6131
+ for (let i = 0; i < binaryString.length; i++) {
6132
+ bytes[i] = binaryString.charCodeAt(i);
6133
+ }
6134
+ pending.resolve({ success: true, data: bytes, mimeType: message2.mimeType });
6135
+ } else {
6136
+ pending.resolve({ success: false, error: message2.error || "Retrieval failed" });
6137
+ }
6138
+ }
6123
6139
  }
6124
6140
  }
6125
6141
  async store(options) {
@@ -6163,6 +6179,32 @@ var StorageWebSocketClient = class {
6163
6179
  }
6164
6180
  this.pendingRequests.clear();
6165
6181
  }
6182
+ async retrieve(cid, timeout = 3e4) {
6183
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
6184
+ await this.connect();
6185
+ }
6186
+ const requestId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
6187
+ const request = {
6188
+ type: "retrieve-request",
6189
+ requestId,
6190
+ cid
6191
+ };
6192
+ return new Promise((resolve, reject) => {
6193
+ const timeoutHandle = setTimeout(() => {
6194
+ this.pendingRequests.delete(requestId);
6195
+ reject(new Error("Retrieval request timeout"));
6196
+ }, timeout);
6197
+ this.pendingRequests.set(requestId, { resolve, reject, timeout: timeoutHandle });
6198
+ try {
6199
+ this.ws.send(JSON.stringify(request));
6200
+ console.log("[Storage WS] Sent retrieval request:", requestId, "CID:", cid);
6201
+ } catch (error) {
6202
+ clearTimeout(timeoutHandle);
6203
+ this.pendingRequests.delete(requestId);
6204
+ reject(error);
6205
+ }
6206
+ });
6207
+ }
6166
6208
  isConnected() {
6167
6209
  return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
6168
6210
  }
@@ -6224,17 +6266,7 @@ var ByteCaveClient = class {
6224
6266
  console.log("[ByteCave] Bootstrap peers:", bootstrapPeers);
6225
6267
  this.node = await createLibp2p({
6226
6268
  transports: [
6227
- webRTC({
6228
- rtcConfiguration: {
6229
- iceServers: [
6230
- // Google public STUN servers for NAT traversal
6231
- { urls: "stun:stun.l.google.com:19302" },
6232
- { urls: "stun:stun1.l.google.com:19302" },
6233
- // Mozilla public STUN server
6234
- { urls: "stun:stun.services.mozilla.com" }
6235
- ]
6236
- }
6237
- }),
6269
+ webRTC(),
6238
6270
  webSockets(),
6239
6271
  circuitRelayTransport()
6240
6272
  ],
@@ -6544,124 +6576,71 @@ Nonce: ${nonce}`;
6544
6576
  console.warn("[ByteCave] Failed to create authorization:", err.message);
6545
6577
  }
6546
6578
  }
6547
- if (this.node) {
6548
- console.log("[ByteCave] Attempting direct P2P storage (with STUN NAT traversal)");
6549
- const allPeers = this.node.getPeers();
6550
- const connectedPeerIds = allPeers.map((p) => p.toString());
6551
- console.log("[ByteCave] Store - connected storage peers:", connectedPeerIds.length);
6552
- if (connectedPeerIds.length > 0) {
6553
- const registeredPeerIds = Array.from(this.knownPeers.values()).filter((p) => p.isRegistered && connectedPeerIds.includes(p.peerId)).map((p) => p.peerId);
6554
- const storagePeerIds = registeredPeerIds.length > 0 ? [...registeredPeerIds, ...connectedPeerIds.filter((id) => !registeredPeerIds.includes(id))] : connectedPeerIds;
6555
- for (const peerId of storagePeerIds) {
6556
- console.log("[ByteCave] Attempting P2P store to peer:", peerId.slice(0, 12) + "...");
6557
- try {
6558
- const result = await p2pProtocolClient.storeToPeer(
6559
- peerId,
6560
- dataArray,
6561
- mimeType || "application/octet-stream",
6562
- authorization,
6563
- false,
6564
- // shouldVerifyOnChain - false for browser test storage
6565
- hashIdToken
6566
- );
6567
- if (result.success && result.cid) {
6568
- console.log("[ByteCave] \u2713 P2P store successful:", result.cid);
6569
- return {
6570
- success: true,
6571
- cid: result.cid,
6572
- peerId
6573
- };
6574
- }
6575
- console.warn("[ByteCave] \u2717 P2P store failed:", result.error);
6576
- } catch (err) {
6577
- console.error("[ByteCave] \u2717 P2P store exception:", err.message);
6578
- }
6579
- }
6580
- }
6581
- console.log("[ByteCave] P2P storage failed or no peers available, falling back to WebSocket relay");
6579
+ if (!this.config.relayWsUrl) {
6580
+ return { success: false, error: "WebSocket relay URL not configured" };
6582
6581
  }
6583
- if (this.config.relayWsUrl) {
6584
- console.log("[ByteCave] Attempting WebSocket storage via relay (fallback)");
6585
- try {
6586
- if (!this.storageWsClient) {
6587
- this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6588
- }
6589
- const wsAuth = authorization ? {
6590
- signature: authorization.signature,
6591
- address: authorization.sender,
6592
- timestamp: authorization.timestamp,
6593
- nonce: authorization.nonce,
6594
- appId: authorization.appId,
6595
- contentHash: authorization.contentHash
6596
- } : void 0;
6597
- const result = await this.storageWsClient.store({
6598
- data: dataArray,
6599
- contentType: mimeType || "application/octet-stream",
6600
- hashIdToken,
6601
- authorization: wsAuth,
6602
- timeout: 3e4
6603
- });
6604
- if (result.success && result.cid) {
6605
- console.log("[ByteCave] \u2713 WebSocket storage successful (fallback):", result.cid);
6606
- return {
6607
- success: true,
6608
- cid: result.cid,
6609
- peerId: "relay-ws"
6610
- };
6611
- }
6612
- console.warn("[ByteCave] WebSocket storage failed:", result.error);
6613
- } catch (err) {
6614
- console.warn("[ByteCave] WebSocket storage exception:", err.message);
6582
+ console.log("[ByteCave] Storing via WebSocket relay");
6583
+ try {
6584
+ if (!this.storageWsClient) {
6585
+ this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6615
6586
  }
6587
+ const wsAuth = authorization ? {
6588
+ signature: authorization.signature,
6589
+ address: authorization.sender,
6590
+ timestamp: authorization.timestamp,
6591
+ nonce: authorization.nonce,
6592
+ appId: authorization.appId,
6593
+ contentHash: authorization.contentHash
6594
+ } : void 0;
6595
+ const result = await this.storageWsClient.store({
6596
+ data: dataArray,
6597
+ contentType: mimeType || "application/octet-stream",
6598
+ hashIdToken,
6599
+ authorization: wsAuth,
6600
+ timeout: 3e4
6601
+ });
6602
+ if (result.success && result.cid) {
6603
+ console.log("[ByteCave] \u2713 WebSocket storage successful:", result.cid);
6604
+ return {
6605
+ success: true,
6606
+ cid: result.cid,
6607
+ peerId: "relay-ws"
6608
+ };
6609
+ }
6610
+ console.warn("[ByteCave] WebSocket storage failed:", result.error);
6611
+ return { success: false, error: result.error || "WebSocket storage failed" };
6612
+ } catch (err) {
6613
+ console.error("[ByteCave] WebSocket storage exception:", err.message);
6614
+ return { success: false, error: err.message };
6616
6615
  }
6617
- return { success: false, error: "All storage methods failed (P2P and WebSocket relay)" };
6618
6616
  }
6619
6617
  /**
6620
- * Retrieve ciphertext from a node via P2P only (no HTTP fallback)
6618
+ * Retrieve ciphertext from a node via WebSocket relay
6621
6619
  */
6622
6620
  async retrieve(cid) {
6623
- if (!this.node) {
6624
- console.error("[ByteCave] Retrieve failed: P2P node not initialized");
6625
- return { success: false, error: "P2P node not initialized" };
6621
+ if (!this.config.relayWsUrl) {
6622
+ return { success: false, error: "WebSocket relay URL not configured" };
6626
6623
  }
6627
- const libp2pPeers = this.node.getPeers();
6628
- console.log("[ByteCave] Retrieve - libp2p peers:", libp2pPeers.length);
6629
- console.log("[ByteCave] Retrieve - known peers:", this.knownPeers.size);
6630
- console.log("[ByteCave] Retrieve - node status:", this.node.status);
6631
- if (libp2pPeers.length === 0) {
6632
- console.warn("[ByteCave] Retrieve failed: No libp2p peers connected, but have", this.knownPeers.size, "known peers");
6633
- return { success: false, error: "No connected peers available" };
6634
- }
6635
- const peersWithCid = [];
6636
- for (const peerId of libp2pPeers) {
6637
- const peerIdStr = peerId.toString();
6638
- try {
6639
- const hasCid = await p2pProtocolClient.peerHasCid(peerIdStr, cid);
6640
- if (hasCid) {
6641
- peersWithCid.push(peerIdStr);
6642
- }
6643
- } catch (error) {
6624
+ console.log("[ByteCave] Retrieving via WebSocket relay, CID:", cid);
6625
+ try {
6626
+ if (!this.storageWsClient) {
6627
+ this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6644
6628
  }
6645
- }
6646
- if (peersWithCid.length === 0) {
6647
- return { success: false, error: "Blob not found on any connected peer" };
6648
- }
6649
- for (const peerId of peersWithCid) {
6650
- try {
6651
- const timeoutPromise = new Promise(
6652
- (_, reject) => setTimeout(() => reject(new Error("Retrieval timeout after 10s")), 1e4)
6653
- );
6654
- const result = await Promise.race([
6655
- p2pProtocolClient.retrieveFromPeer(peerId, cid),
6656
- timeoutPromise
6657
- ]);
6658
- if (result) {
6659
- return { success: true, data: result.data, peerId };
6660
- }
6661
- } catch (error) {
6629
+ const result = await this.storageWsClient.retrieve(cid, 3e4);
6630
+ if (result.success && result.data) {
6631
+ console.log("[ByteCave] \u2713 WebSocket retrieval successful:", cid);
6632
+ return {
6633
+ success: true,
6634
+ data: result.data,
6635
+ peerId: "relay-ws"
6636
+ };
6662
6637
  }
6638
+ console.warn("[ByteCave] WebSocket retrieval failed:", result.error);
6639
+ return { success: false, error: result.error || "Retrieval failed" };
6640
+ } catch (err) {
6641
+ console.error("[ByteCave] WebSocket retrieval exception:", err.message);
6642
+ return { success: false, error: err.message };
6663
6643
  }
6664
- return { success: false, error: "Failed to retrieve blob from peers that have it" };
6665
6644
  }
6666
6645
  /**
6667
6646
  * Register content in ContentRegistry contract (on-chain)
package/dist/client.d.ts CHANGED
@@ -39,7 +39,7 @@ export declare class ByteCaveClient {
39
39
  */
40
40
  store(data: Uint8Array | ArrayBuffer, mimeType?: string, signer?: any, hashIdToken?: number): Promise<StoreResult>;
41
41
  /**
42
- * Retrieve ciphertext from a node via P2P only (no HTTP fallback)
42
+ * Retrieve ciphertext from a node via WebSocket relay
43
43
  */
44
44
  retrieve(cid: string): Promise<RetrieveResult>;
45
45
  /**
package/dist/index.cjs CHANGED
@@ -6173,6 +6173,22 @@ var StorageWebSocketClient = class {
6173
6173
  pending.resolve({ success: false, error: message2.error || "Storage failed" });
6174
6174
  }
6175
6175
  }
6176
+ } else if (message2.type === "retrieve-response") {
6177
+ const pending = this.pendingRequests.get(message2.requestId);
6178
+ if (pending) {
6179
+ clearTimeout(pending.timeout);
6180
+ this.pendingRequests.delete(message2.requestId);
6181
+ if (message2.success && message2.data) {
6182
+ const binaryString = atob(message2.data);
6183
+ const bytes = new Uint8Array(binaryString.length);
6184
+ for (let i = 0; i < binaryString.length; i++) {
6185
+ bytes[i] = binaryString.charCodeAt(i);
6186
+ }
6187
+ pending.resolve({ success: true, data: bytes, mimeType: message2.mimeType });
6188
+ } else {
6189
+ pending.resolve({ success: false, error: message2.error || "Retrieval failed" });
6190
+ }
6191
+ }
6176
6192
  }
6177
6193
  }
6178
6194
  async store(options) {
@@ -6216,6 +6232,32 @@ var StorageWebSocketClient = class {
6216
6232
  }
6217
6233
  this.pendingRequests.clear();
6218
6234
  }
6235
+ async retrieve(cid, timeout = 3e4) {
6236
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
6237
+ await this.connect();
6238
+ }
6239
+ const requestId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
6240
+ const request = {
6241
+ type: "retrieve-request",
6242
+ requestId,
6243
+ cid
6244
+ };
6245
+ return new Promise((resolve, reject) => {
6246
+ const timeoutHandle = setTimeout(() => {
6247
+ this.pendingRequests.delete(requestId);
6248
+ reject(new Error("Retrieval request timeout"));
6249
+ }, timeout);
6250
+ this.pendingRequests.set(requestId, { resolve, reject, timeout: timeoutHandle });
6251
+ try {
6252
+ this.ws.send(JSON.stringify(request));
6253
+ console.log("[Storage WS] Sent retrieval request:", requestId, "CID:", cid);
6254
+ } catch (error) {
6255
+ clearTimeout(timeoutHandle);
6256
+ this.pendingRequests.delete(requestId);
6257
+ reject(error);
6258
+ }
6259
+ });
6260
+ }
6219
6261
  isConnected() {
6220
6262
  return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
6221
6263
  }
@@ -6277,17 +6319,7 @@ var ByteCaveClient = class {
6277
6319
  console.log("[ByteCave] Bootstrap peers:", bootstrapPeers);
6278
6320
  this.node = await (0, import_libp2p.createLibp2p)({
6279
6321
  transports: [
6280
- (0, import_webrtc.webRTC)({
6281
- rtcConfiguration: {
6282
- iceServers: [
6283
- // Google public STUN servers for NAT traversal
6284
- { urls: "stun:stun.l.google.com:19302" },
6285
- { urls: "stun:stun1.l.google.com:19302" },
6286
- // Mozilla public STUN server
6287
- { urls: "stun:stun.services.mozilla.com" }
6288
- ]
6289
- }
6290
- }),
6322
+ (0, import_webrtc.webRTC)(),
6291
6323
  (0, import_websockets.webSockets)(),
6292
6324
  (0, import_circuit_relay_v2.circuitRelayTransport)()
6293
6325
  ],
@@ -6597,124 +6629,71 @@ Nonce: ${nonce}`;
6597
6629
  console.warn("[ByteCave] Failed to create authorization:", err.message);
6598
6630
  }
6599
6631
  }
6600
- if (this.node) {
6601
- console.log("[ByteCave] Attempting direct P2P storage (with STUN NAT traversal)");
6602
- const allPeers = this.node.getPeers();
6603
- const connectedPeerIds = allPeers.map((p) => p.toString());
6604
- console.log("[ByteCave] Store - connected storage peers:", connectedPeerIds.length);
6605
- if (connectedPeerIds.length > 0) {
6606
- const registeredPeerIds = Array.from(this.knownPeers.values()).filter((p) => p.isRegistered && connectedPeerIds.includes(p.peerId)).map((p) => p.peerId);
6607
- const storagePeerIds = registeredPeerIds.length > 0 ? [...registeredPeerIds, ...connectedPeerIds.filter((id) => !registeredPeerIds.includes(id))] : connectedPeerIds;
6608
- for (const peerId of storagePeerIds) {
6609
- console.log("[ByteCave] Attempting P2P store to peer:", peerId.slice(0, 12) + "...");
6610
- try {
6611
- const result = await p2pProtocolClient.storeToPeer(
6612
- peerId,
6613
- dataArray,
6614
- mimeType || "application/octet-stream",
6615
- authorization,
6616
- false,
6617
- // shouldVerifyOnChain - false for browser test storage
6618
- hashIdToken
6619
- );
6620
- if (result.success && result.cid) {
6621
- console.log("[ByteCave] \u2713 P2P store successful:", result.cid);
6622
- return {
6623
- success: true,
6624
- cid: result.cid,
6625
- peerId
6626
- };
6627
- }
6628
- console.warn("[ByteCave] \u2717 P2P store failed:", result.error);
6629
- } catch (err) {
6630
- console.error("[ByteCave] \u2717 P2P store exception:", err.message);
6631
- }
6632
- }
6633
- }
6634
- console.log("[ByteCave] P2P storage failed or no peers available, falling back to WebSocket relay");
6632
+ if (!this.config.relayWsUrl) {
6633
+ return { success: false, error: "WebSocket relay URL not configured" };
6635
6634
  }
6636
- if (this.config.relayWsUrl) {
6637
- console.log("[ByteCave] Attempting WebSocket storage via relay (fallback)");
6638
- try {
6639
- if (!this.storageWsClient) {
6640
- this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6641
- }
6642
- const wsAuth = authorization ? {
6643
- signature: authorization.signature,
6644
- address: authorization.sender,
6645
- timestamp: authorization.timestamp,
6646
- nonce: authorization.nonce,
6647
- appId: authorization.appId,
6648
- contentHash: authorization.contentHash
6649
- } : void 0;
6650
- const result = await this.storageWsClient.store({
6651
- data: dataArray,
6652
- contentType: mimeType || "application/octet-stream",
6653
- hashIdToken,
6654
- authorization: wsAuth,
6655
- timeout: 3e4
6656
- });
6657
- if (result.success && result.cid) {
6658
- console.log("[ByteCave] \u2713 WebSocket storage successful (fallback):", result.cid);
6659
- return {
6660
- success: true,
6661
- cid: result.cid,
6662
- peerId: "relay-ws"
6663
- };
6664
- }
6665
- console.warn("[ByteCave] WebSocket storage failed:", result.error);
6666
- } catch (err) {
6667
- console.warn("[ByteCave] WebSocket storage exception:", err.message);
6635
+ console.log("[ByteCave] Storing via WebSocket relay");
6636
+ try {
6637
+ if (!this.storageWsClient) {
6638
+ this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6668
6639
  }
6640
+ const wsAuth = authorization ? {
6641
+ signature: authorization.signature,
6642
+ address: authorization.sender,
6643
+ timestamp: authorization.timestamp,
6644
+ nonce: authorization.nonce,
6645
+ appId: authorization.appId,
6646
+ contentHash: authorization.contentHash
6647
+ } : void 0;
6648
+ const result = await this.storageWsClient.store({
6649
+ data: dataArray,
6650
+ contentType: mimeType || "application/octet-stream",
6651
+ hashIdToken,
6652
+ authorization: wsAuth,
6653
+ timeout: 3e4
6654
+ });
6655
+ if (result.success && result.cid) {
6656
+ console.log("[ByteCave] \u2713 WebSocket storage successful:", result.cid);
6657
+ return {
6658
+ success: true,
6659
+ cid: result.cid,
6660
+ peerId: "relay-ws"
6661
+ };
6662
+ }
6663
+ console.warn("[ByteCave] WebSocket storage failed:", result.error);
6664
+ return { success: false, error: result.error || "WebSocket storage failed" };
6665
+ } catch (err) {
6666
+ console.error("[ByteCave] WebSocket storage exception:", err.message);
6667
+ return { success: false, error: err.message };
6669
6668
  }
6670
- return { success: false, error: "All storage methods failed (P2P and WebSocket relay)" };
6671
6669
  }
6672
6670
  /**
6673
- * Retrieve ciphertext from a node via P2P only (no HTTP fallback)
6671
+ * Retrieve ciphertext from a node via WebSocket relay
6674
6672
  */
6675
6673
  async retrieve(cid) {
6676
- if (!this.node) {
6677
- console.error("[ByteCave] Retrieve failed: P2P node not initialized");
6678
- return { success: false, error: "P2P node not initialized" };
6674
+ if (!this.config.relayWsUrl) {
6675
+ return { success: false, error: "WebSocket relay URL not configured" };
6679
6676
  }
6680
- const libp2pPeers = this.node.getPeers();
6681
- console.log("[ByteCave] Retrieve - libp2p peers:", libp2pPeers.length);
6682
- console.log("[ByteCave] Retrieve - known peers:", this.knownPeers.size);
6683
- console.log("[ByteCave] Retrieve - node status:", this.node.status);
6684
- if (libp2pPeers.length === 0) {
6685
- console.warn("[ByteCave] Retrieve failed: No libp2p peers connected, but have", this.knownPeers.size, "known peers");
6686
- return { success: false, error: "No connected peers available" };
6687
- }
6688
- const peersWithCid = [];
6689
- for (const peerId of libp2pPeers) {
6690
- const peerIdStr = peerId.toString();
6691
- try {
6692
- const hasCid = await p2pProtocolClient.peerHasCid(peerIdStr, cid);
6693
- if (hasCid) {
6694
- peersWithCid.push(peerIdStr);
6695
- }
6696
- } catch (error) {
6677
+ console.log("[ByteCave] Retrieving via WebSocket relay, CID:", cid);
6678
+ try {
6679
+ if (!this.storageWsClient) {
6680
+ this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
6697
6681
  }
6698
- }
6699
- if (peersWithCid.length === 0) {
6700
- return { success: false, error: "Blob not found on any connected peer" };
6701
- }
6702
- for (const peerId of peersWithCid) {
6703
- try {
6704
- const timeoutPromise = new Promise(
6705
- (_, reject) => setTimeout(() => reject(new Error("Retrieval timeout after 10s")), 1e4)
6706
- );
6707
- const result = await Promise.race([
6708
- p2pProtocolClient.retrieveFromPeer(peerId, cid),
6709
- timeoutPromise
6710
- ]);
6711
- if (result) {
6712
- return { success: true, data: result.data, peerId };
6713
- }
6714
- } catch (error) {
6682
+ const result = await this.storageWsClient.retrieve(cid, 3e4);
6683
+ if (result.success && result.data) {
6684
+ console.log("[ByteCave] \u2713 WebSocket retrieval successful:", cid);
6685
+ return {
6686
+ success: true,
6687
+ data: result.data,
6688
+ peerId: "relay-ws"
6689
+ };
6715
6690
  }
6691
+ console.warn("[ByteCave] WebSocket retrieval failed:", result.error);
6692
+ return { success: false, error: result.error || "Retrieval failed" };
6693
+ } catch (err) {
6694
+ console.error("[ByteCave] WebSocket retrieval exception:", err.message);
6695
+ return { success: false, error: err.message };
6716
6696
  }
6717
- return { success: false, error: "Failed to retrieve blob from peers that have it" };
6718
6697
  }
6719
6698
  /**
6720
6699
  * Register content in ContentRegistry contract (on-chain)
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  useHashdImage,
14
14
  useHashdMedia,
15
15
  useHashdUrl
16
- } from "./chunk-SIGWRCA5.js";
16
+ } from "./chunk-DOBNBGXF.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-SIGWRCA5.js";
11
+ } from "../chunk-DOBNBGXF.js";
12
12
  import "../chunk-EEZWRIUI.js";
13
13
  export {
14
14
  HashdAudio,
@@ -29,5 +29,11 @@ export declare class StorageWebSocketClient {
29
29
  error?: string;
30
30
  }>;
31
31
  disconnect(): void;
32
+ retrieve(cid: string, timeout?: number): Promise<{
33
+ success: boolean;
34
+ data?: Uint8Array;
35
+ mimeType?: string;
36
+ error?: string;
37
+ }>;
32
38
  isConnected(): boolean;
33
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethashd/bytecave-browser",
3
- "version": "1.0.39",
3
+ "version": "1.0.41",
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",
package/src/client.ts CHANGED
@@ -99,17 +99,7 @@ export class ByteCaveClient {
99
99
  // Create libp2p node with WebRTC transport
100
100
  this.node = await createLibp2p({
101
101
  transports: [
102
- webRTC({
103
- rtcConfiguration: {
104
- iceServers: [
105
- // Google public STUN servers for NAT traversal
106
- { urls: 'stun:stun.l.google.com:19302' },
107
- { urls: 'stun:stun1.l.google.com:19302' },
108
- // Mozilla public STUN server
109
- { urls: 'stun:stun.services.mozilla.com' }
110
- ]
111
- }
112
- }) as any,
102
+ webRTC() as any,
113
103
  webSockets() as any,
114
104
  circuitRelayTransport() as any
115
105
  ],
@@ -486,162 +476,84 @@ Nonce: ${nonce}`;
486
476
  }
487
477
  }
488
478
 
489
- // Try direct P2P storage first (with STUN for NAT traversal)
490
- if (this.node) {
491
- console.log('[ByteCave] Attempting direct P2P storage (with STUN NAT traversal)');
492
-
493
- const allPeers = this.node.getPeers();
494
- const connectedPeerIds = allPeers.map(p => p.toString());
495
-
496
- console.log('[ByteCave] Store - connected storage peers:', connectedPeerIds.length);
497
-
498
- if (connectedPeerIds.length > 0) {
499
- // Prioritize registered peers from knownPeers
500
- const registeredPeerIds = Array.from(this.knownPeers.values())
501
- .filter(p => p.isRegistered && connectedPeerIds.includes(p.peerId))
502
- .map(p => p.peerId);
503
-
504
- const storagePeerIds = registeredPeerIds.length > 0
505
- ? [...registeredPeerIds, ...connectedPeerIds.filter(id => !registeredPeerIds.includes(id))]
506
- : connectedPeerIds;
507
-
508
- for (const peerId of storagePeerIds) {
509
- console.log('[ByteCave] Attempting P2P store to peer:', peerId.slice(0, 12) + '...');
510
-
511
- try {
512
- const result = await p2pProtocolClient.storeToPeer(
513
- peerId,
514
- dataArray,
515
- mimeType || 'application/octet-stream',
516
- authorization,
517
- false, // shouldVerifyOnChain - false for browser test storage
518
- hashIdToken
519
- );
520
-
521
- if (result.success && result.cid) {
522
- console.log('[ByteCave] ✓ P2P store successful:', result.cid);
523
- return {
524
- success: true,
525
- cid: result.cid,
526
- peerId
527
- };
528
- }
529
-
530
- console.warn('[ByteCave] ✗ P2P store failed:', result.error);
531
- } catch (err: any) {
532
- console.error('[ByteCave] ✗ P2P store exception:', err.message);
533
- }
534
- }
535
- }
536
-
537
- console.log('[ByteCave] P2P storage failed or no peers available, falling back to WebSocket relay');
479
+ // Use WebSocket relay for storage (browsers cannot use P2P storage protocol)
480
+ if (!this.config.relayWsUrl) {
481
+ return { success: false, error: 'WebSocket relay URL not configured' };
538
482
  }
539
483
 
540
- // Fallback to WebSocket relay if P2P fails or node not initialized
541
- if (this.config.relayWsUrl) {
542
- console.log('[ByteCave] Attempting WebSocket storage via relay (fallback)');
543
-
544
- try {
545
- if (!this.storageWsClient) {
546
- this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
547
- }
484
+ console.log('[ByteCave] Storing via WebSocket relay');
485
+
486
+ try {
487
+ if (!this.storageWsClient) {
488
+ this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
489
+ }
548
490
 
549
- const wsAuth = authorization ? {
550
- signature: authorization.signature,
551
- address: authorization.sender,
552
- timestamp: authorization.timestamp,
553
- nonce: authorization.nonce,
554
- appId: authorization.appId,
555
- contentHash: authorization.contentHash
556
- } : undefined;
557
-
558
- const result = await this.storageWsClient.store({
559
- data: dataArray,
560
- contentType: mimeType || 'application/octet-stream',
561
- hashIdToken,
562
- authorization: wsAuth,
563
- timeout: 30000
564
- });
565
-
566
- if (result.success && result.cid) {
567
- console.log('[ByteCave] ✓ WebSocket storage successful (fallback):', result.cid);
568
- return {
569
- success: true,
570
- cid: result.cid,
571
- peerId: 'relay-ws'
572
- };
573
- }
491
+ const wsAuth = authorization ? {
492
+ signature: authorization.signature,
493
+ address: authorization.sender,
494
+ timestamp: authorization.timestamp,
495
+ nonce: authorization.nonce,
496
+ appId: authorization.appId,
497
+ contentHash: authorization.contentHash
498
+ } : undefined;
499
+
500
+ const result = await this.storageWsClient.store({
501
+ data: dataArray,
502
+ contentType: mimeType || 'application/octet-stream',
503
+ hashIdToken,
504
+ authorization: wsAuth,
505
+ timeout: 30000
506
+ });
574
507
 
575
- console.warn('[ByteCave] WebSocket storage failed:', result.error);
576
- } catch (err: any) {
577
- console.warn('[ByteCave] WebSocket storage exception:', err.message);
508
+ if (result.success && result.cid) {
509
+ console.log('[ByteCave] WebSocket storage successful:', result.cid);
510
+ return {
511
+ success: true,
512
+ cid: result.cid,
513
+ peerId: 'relay-ws'
514
+ };
578
515
  }
516
+
517
+ console.warn('[ByteCave] WebSocket storage failed:', result.error);
518
+ return { success: false, error: result.error || 'WebSocket storage failed' };
519
+ } catch (err: any) {
520
+ console.error('[ByteCave] WebSocket storage exception:', err.message);
521
+ return { success: false, error: err.message };
579
522
  }
580
-
581
- return { success: false, error: 'All storage methods failed (P2P and WebSocket relay)' };
582
523
  }
583
524
 
584
525
  /**
585
- * Retrieve ciphertext from a node via P2P only (no HTTP fallback)
526
+ * Retrieve ciphertext from a node via WebSocket relay
586
527
  */
587
528
  async retrieve(cid: string): Promise<RetrieveResult> {
588
- if (!this.node) {
589
- console.error('[ByteCave] Retrieve failed: P2P node not initialized');
590
- return { success: false, error: 'P2P node not initialized' };
529
+ if (!this.config.relayWsUrl) {
530
+ return { success: false, error: 'WebSocket relay URL not configured' };
591
531
  }
592
532
 
593
- const libp2pPeers = this.node.getPeers();
594
- console.log('[ByteCave] Retrieve - libp2p peers:', libp2pPeers.length);
595
- console.log('[ByteCave] Retrieve - known peers:', this.knownPeers.size);
596
- console.log('[ByteCave] Retrieve - node status:', this.node.status);
533
+ console.log('[ByteCave] Retrieving via WebSocket relay, CID:', cid);
597
534
 
598
- if (libp2pPeers.length === 0) {
599
- console.warn('[ByteCave] Retrieve failed: No libp2p peers connected, but have', this.knownPeers.size, 'known peers');
600
- return { success: false, error: 'No connected peers available' };
601
- }
602
-
603
- // Find which peers have this CID
604
- const peersWithCid: string[] = [];
605
-
606
- for (const peerId of libp2pPeers) {
607
- const peerIdStr = peerId.toString();
608
-
609
- try {
610
- const hasCid = await p2pProtocolClient.peerHasCid(peerIdStr, cid);
611
-
612
- if (hasCid) {
613
- peersWithCid.push(peerIdStr);
614
- }
615
- } catch (error: any) {
616
- // Skip peers that don't support the protocol
535
+ try {
536
+ if (!this.storageWsClient) {
537
+ this.storageWsClient = new StorageWebSocketClient(this.config.relayWsUrl);
617
538
  }
618
- }
619
539
 
620
- if (peersWithCid.length === 0) {
621
- return { success: false, error: 'Blob not found on any connected peer' };
622
- }
540
+ const result = await this.storageWsClient.retrieve(cid, 30000);
623
541
 
624
- // Try to retrieve from peers that have the CID
625
- for (const peerId of peersWithCid) {
626
- try {
627
- const timeoutPromise = new Promise<null>((_, reject) =>
628
- setTimeout(() => reject(new Error('Retrieval timeout after 10s')), 10000)
629
- );
630
-
631
- const result = await Promise.race([
632
- p2pProtocolClient.retrieveFromPeer(peerId, cid),
633
- timeoutPromise
634
- ]);
635
-
636
- if (result) {
637
- return { success: true, data: result.data, peerId };
638
- }
639
- } catch (error: any) {
640
- // Continue to next peer
542
+ if (result.success && result.data) {
543
+ console.log('[ByteCave] WebSocket retrieval successful:', cid);
544
+ return {
545
+ success: true,
546
+ data: result.data,
547
+ peerId: 'relay-ws'
548
+ };
641
549
  }
642
- }
643
550
 
644
- return { success: false, error: 'Failed to retrieve blob from peers that have it' };
551
+ console.warn('[ByteCave] WebSocket retrieval failed:', result.error);
552
+ return { success: false, error: result.error || 'Retrieval failed' };
553
+ } catch (err: any) {
554
+ console.error('[ByteCave] WebSocket retrieval exception:', err.message);
555
+ return { success: false, error: err.message };
556
+ }
645
557
  }
646
558
 
647
559
  /**
@@ -27,7 +27,22 @@ interface StorageResponseMessage {
27
27
  error?: string;
28
28
  }
29
29
 
30
- type Message = StorageRequestMessage | StorageResponseMessage;
30
+ interface RetrieveRequestMessage {
31
+ type: 'retrieve-request';
32
+ requestId: string;
33
+ cid: string;
34
+ }
35
+
36
+ interface RetrieveResponseMessage {
37
+ type: 'retrieve-response';
38
+ requestId: string;
39
+ success: boolean;
40
+ data?: string; // base64
41
+ mimeType?: string;
42
+ error?: string;
43
+ }
44
+
45
+ type Message = StorageRequestMessage | StorageResponseMessage | RetrieveRequestMessage | RetrieveResponseMessage;
31
46
 
32
47
  export interface StoreViaWebSocketOptions {
33
48
  data: Uint8Array;
@@ -47,7 +62,7 @@ export interface StoreViaWebSocketOptions {
47
62
  export class StorageWebSocketClient {
48
63
  private ws: WebSocket | null = null;
49
64
  private pendingRequests: Map<string, {
50
- resolve: (result: { success: boolean; cid?: string; error?: string }) => void;
65
+ resolve: (result: any) => void;
51
66
  reject: (error: Error) => void;
52
67
  timeout: NodeJS.Timeout;
53
68
  }> = new Map();
@@ -107,6 +122,24 @@ export class StorageWebSocketClient {
107
122
  pending.resolve({ success: false, error: message.error || 'Storage failed' });
108
123
  }
109
124
  }
125
+ } else if (message.type === 'retrieve-response') {
126
+ const pending = this.pendingRequests.get(message.requestId);
127
+ if (pending) {
128
+ clearTimeout(pending.timeout);
129
+ this.pendingRequests.delete(message.requestId);
130
+
131
+ if (message.success && message.data) {
132
+ // Convert base64 to Uint8Array
133
+ const binaryString = atob(message.data);
134
+ const bytes = new Uint8Array(binaryString.length);
135
+ for (let i = 0; i < binaryString.length; i++) {
136
+ bytes[i] = binaryString.charCodeAt(i);
137
+ }
138
+ pending.resolve({ success: true, data: bytes, mimeType: message.mimeType });
139
+ } else {
140
+ pending.resolve({ success: false, error: message.error || 'Retrieval failed' });
141
+ }
142
+ }
110
143
  }
111
144
  }
112
145
 
@@ -163,6 +196,39 @@ export class StorageWebSocketClient {
163
196
  this.pendingRequests.clear();
164
197
  }
165
198
 
199
+ async retrieve(cid: string, timeout: number = 30000): Promise<{ success: boolean; data?: Uint8Array; mimeType?: string; error?: string }> {
200
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
201
+ await this.connect();
202
+ }
203
+
204
+ const requestId = Math.random().toString(36).substring(2, 15) +
205
+ Math.random().toString(36).substring(2, 15);
206
+
207
+ const request: RetrieveRequestMessage = {
208
+ type: 'retrieve-request',
209
+ requestId,
210
+ cid
211
+ };
212
+
213
+ return new Promise((resolve, reject) => {
214
+ const timeoutHandle = setTimeout(() => {
215
+ this.pendingRequests.delete(requestId);
216
+ reject(new Error('Retrieval request timeout'));
217
+ }, timeout);
218
+
219
+ this.pendingRequests.set(requestId, { resolve, reject, timeout: timeoutHandle });
220
+
221
+ try {
222
+ this.ws!.send(JSON.stringify(request));
223
+ console.log('[Storage WS] Sent retrieval request:', requestId, 'CID:', cid);
224
+ } catch (error: any) {
225
+ clearTimeout(timeoutHandle);
226
+ this.pendingRequests.delete(requestId);
227
+ reject(error);
228
+ }
229
+ });
230
+ }
231
+
166
232
  isConnected(): boolean {
167
233
  return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
168
234
  }