@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.
- package/dist/{chunk-SIGWRCA5.js → chunk-DOBNBGXF.js} +97 -118
- package/dist/client.d.ts +1 -1
- package/dist/index.cjs +97 -118
- package/dist/index.js +1 -1
- package/dist/react/index.js +1 -1
- package/dist/storage-websocket.d.ts +6 -0
- package/package.json +1 -1
- package/src/client.ts +60 -148
- package/src/storage-websocket.ts +68 -2
|
@@ -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.
|
|
6548
|
-
|
|
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
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
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
|
|
6618
|
+
* Retrieve ciphertext from a node via WebSocket relay
|
|
6621
6619
|
*/
|
|
6622
6620
|
async retrieve(cid) {
|
|
6623
|
-
if (!this.
|
|
6624
|
-
|
|
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
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
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
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
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
|
|
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.
|
|
6601
|
-
|
|
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
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
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
|
|
6671
|
+
* Retrieve ciphertext from a node via WebSocket relay
|
|
6674
6672
|
*/
|
|
6675
6673
|
async retrieve(cid) {
|
|
6676
|
-
if (!this.
|
|
6677
|
-
|
|
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
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
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
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
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
package/dist/react/index.js
CHANGED
|
@@ -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
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
|
-
//
|
|
490
|
-
if (this.
|
|
491
|
-
|
|
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
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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
|
|
526
|
+
* Retrieve ciphertext from a node via WebSocket relay
|
|
586
527
|
*/
|
|
587
528
|
async retrieve(cid: string): Promise<RetrieveResult> {
|
|
588
|
-
if (!this.
|
|
589
|
-
|
|
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
|
-
|
|
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
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
-
|
|
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
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
-
|
|
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
|
/**
|
package/src/storage-websocket.ts
CHANGED
|
@@ -27,7 +27,22 @@ interface StorageResponseMessage {
|
|
|
27
27
|
error?: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
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:
|
|
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
|
}
|