@gethashd/bytecave-browser 1.0.1

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/README.md ADDED
@@ -0,0 +1,699 @@
1
+ # ByteCave Browser Client
2
+
3
+ Browser-compatible P2P client library for connecting to the ByteCave decentralized storage network via WebRTC and WebSockets.
4
+
5
+ ## Features
6
+
7
+ - **Fast Node Discovery** - Discover storage nodes in 1-2 seconds via relay peer directory protocol
8
+ - **WebRTC P2P Connections** - Direct browser-to-node connections using WebRTC transport (via libp2p)
9
+ - **Circuit Relay Fallback** - Relay-based P2P connections for NAT traversal when direct WebRTC fails
10
+ - **Pure P2P Architecture** - All communication via libp2p protocols (no HTTP endpoints required)
11
+ - **FloodSub Announcements** - Peer discovery and announcements via pubsub
12
+ - **Contract Integration** - Optional on-chain node registry verification
13
+ - **HashD Protocol** - Custom `hashd://` URL scheme with caching and helpers
14
+ - **React Hooks & Components** - Ready-to-use React integration
15
+ - **TypeScript** - Full type safety
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @gethashd/bytecave-browser
21
+ # or
22
+ yarn add @gethashd/bytecave-browser
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```typescript
28
+ import { ByteCaveClient } from '@gethashd/bytecave-browser';
29
+
30
+ // Initialize client
31
+ const client = new ByteCaveClient({
32
+ appId: 'my-app', // Required: Application identifier
33
+ relayPeers: [
34
+ '/dns4/relay.example.com/tcp/4002/ws/p2p/12D3KooW...'
35
+ ],
36
+ vaultNodeRegistryAddress: '0x...', // VaultNodeRegistry contract
37
+ contentRegistryAddress: '0x...', // ContentRegistry contract
38
+ rpcUrl: 'https://...', // Ethereum RPC URL
39
+ maxPeers: 10,
40
+ connectionTimeout: 30000
41
+ });
42
+
43
+ // Start P2P client
44
+ await client.start();
45
+
46
+ // Store data
47
+ const result = await client.store(
48
+ new TextEncoder().encode('Hello ByteCave!'),
49
+ 'text/plain'
50
+ );
51
+ console.log('Stored with CID:', result.cid);
52
+
53
+ // Retrieve data
54
+ const retrieved = await client.retrieve(result.cid);
55
+ console.log('Retrieved:', new TextDecoder().decode(retrieved.data));
56
+
57
+ // Stop client
58
+ await client.stop();
59
+ ```
60
+
61
+ ## Configuration
62
+
63
+ ### ByteCaveConfig
64
+
65
+ ```typescript
66
+ interface ByteCaveConfig {
67
+ appId: string; // Required: Application identifier for storage authorization
68
+ relayPeers?: string[]; // Relay node multiaddrs for P2P connections
69
+ directNodeAddrs?: string[]; // Direct node multiaddrs (WebRTC without relay)
70
+ vaultNodeRegistryAddress?: string; // Optional: VaultNodeRegistry contract for node verification
71
+ contentRegistryAddress?: string; // Optional: ContentRegistry contract
72
+ rpcUrl?: string; // Optional: Ethereum RPC (required if using contracts)
73
+ maxPeers?: number; // Maximum peer connections (default: 10)
74
+ connectionTimeout?: number; // Connection timeout in ms (default: 30000)
75
+ }
76
+ ```
77
+
78
+ ### Relay Peer Configuration
79
+
80
+ **Required**: At least one relay peer multiaddr for P2P discovery.
81
+
82
+ ```typescript
83
+ relayPeers: [
84
+ // WebSocket multiaddr (required for browsers)
85
+ '/dns4/relay.example.com/tcp/4002/ws/p2p/12D3KooW...',
86
+ // Multiple relays for redundancy (recommended)
87
+ '/dns4/relay2.example.com/tcp/4002/ws/p2p/12D3KooW...'
88
+ ]
89
+ ```
90
+
91
+ **Getting Relay Multiaddrs:**
92
+
93
+ From relay HTTP info endpoint:
94
+ ```bash
95
+ curl http://relay.example.com:9090/info
96
+ ```
97
+
98
+ Or from relay node logs:
99
+ ```bash
100
+ docker-compose logs relay1 | grep "Listening on"
101
+ ```
102
+
103
+ **Important**: Use the WebSocket address (contains `/ws/`) for browser clients.
104
+
105
+ ## API Reference
106
+
107
+ ### ByteCaveClient
108
+
109
+ #### Constructor
110
+
111
+ ```typescript
112
+ new ByteCaveClient(config: ByteCaveConfig)
113
+ ```
114
+
115
+ #### Methods
116
+
117
+ **`start(): Promise<void>`**
118
+
119
+ Start the P2P client and connect to relay nodes.
120
+
121
+ ```typescript
122
+ await client.start();
123
+ ```
124
+
125
+ **`stop(): Promise<void>`**
126
+
127
+ Stop the P2P client and disconnect from all peers.
128
+
129
+ ```typescript
130
+ await client.stop();
131
+ ```
132
+
133
+ **`store(data: Uint8Array, mimeType?: string, signer?: any): Promise<StoreResult>`**
134
+
135
+ Store data on the network.
136
+
137
+ ```typescript
138
+ const result = await client.store(data, 'text/plain', signer);
139
+ // Returns: { success: true, cid: '...', peerId: '...' }
140
+ ```
141
+
142
+ **Note:** `mimeType` is optional and defaults to `'application/octet-stream'`. Use standard MIME types like `'text/plain'`, `'image/jpeg'`, `'application/json'`, etc.
143
+
144
+ **`retrieve(cid: string): Promise<RetrieveResult>`**
145
+
146
+ Retrieve data from the network.
147
+
148
+ ```typescript
149
+ const result = await client.retrieve('bafybei...');
150
+ // Returns: { success: true, data: Uint8Array, peerId: '...' }
151
+ ```
152
+
153
+ **`getPeers(): PeerInfo[]`**
154
+
155
+ Get list of connected peers.
156
+
157
+ ```typescript
158
+ const peers = client.getPeers();
159
+ // Returns: [{ peerId: '...', publicKey: '...', connected: true, ... }]
160
+ ```
161
+
162
+ **`getConnectionState(): ConnectionState`**
163
+
164
+ Get current connection state.
165
+
166
+ ```typescript
167
+ const state = client.getConnectionState();
168
+ // Returns: 'disconnected' | 'connecting' | 'connected' | 'error'
169
+ ```
170
+
171
+ #### Events
172
+
173
+ **`on(event: string, callback: Function): void`**
174
+
175
+ Listen for events.
176
+
177
+ ```typescript
178
+ client.on('connectionStateChange', (state) => {
179
+ console.log('Connection state:', state);
180
+ });
181
+
182
+ client.on('peerConnect', (peer) => {
183
+ console.log('Peer connected:', peer.peerId);
184
+ });
185
+
186
+ client.on('peerDisconnect', (peerId) => {
187
+ console.log('Peer disconnected:', peerId);
188
+ });
189
+
190
+ client.on('peerAnnounce', (peer) => {
191
+ console.log('Peer announced:', peer.peerId);
192
+ });
193
+ ```
194
+
195
+ **`off(event: string, callback: Function): void`**
196
+
197
+ Remove event listener.
198
+
199
+ ```typescript
200
+ client.off('peerConnect', callback);
201
+ ```
202
+
203
+ ## HashD Protocol (`hashd://`)
204
+
205
+ Custom URL scheme for loading content from ByteCave network. **Note:** `hashd://` URLs are not automatically resolved by browsers - you must use the provided utilities to parse and fetch content.
206
+
207
+ ### URL Format
208
+
209
+ ```
210
+ hashd://{cid}
211
+ hashd://{cid}?type=image/png
212
+ hashd://{cid}?type=image/png&decrypt=true
213
+ ```
214
+
215
+ ### Core Functions
216
+
217
+ ```typescript
218
+ import {
219
+ parseHashdUrl,
220
+ createHashdUrl,
221
+ fetchHashdContent,
222
+ prefetchHashdContent,
223
+ clearHashdCache,
224
+ getHashdCacheStats,
225
+ revokeHashdUrl
226
+ } from '@gethashd/bytecave-browser';
227
+
228
+ // Parse hashd:// URL into components
229
+ const parsed = parseHashdUrl('hashd://bafybei...?type=image/png');
230
+ // { protocol: 'hashd:', cid: 'bafybei...', mimeType: 'image/png', raw: '...' }
231
+
232
+ // Create hashd:// URL from CID
233
+ const url = createHashdUrl('bafybei...', { mimeType: 'image/png' });
234
+ // 'hashd://bafybei...?type=image/png'
235
+
236
+ // Fetch content and get blob URL (with automatic caching)
237
+ const result = await fetchHashdContent(url, client);
238
+ // { data: Uint8Array, blobUrl: 'blob:...', mimeType: 'image/png', cached: false }
239
+
240
+ // Prefetch and cache content
241
+ await prefetchHashdContent('hashd://bafybei...', client);
242
+
243
+ // Cache management
244
+ const stats = getHashdCacheStats(); // { size: 5 }
245
+ clearHashdCache(); // Clear all cached blob URLs
246
+ revokeHashdUrl('bafybei...'); // Revoke specific blob URL
247
+ ```
248
+
249
+ ### How It Works
250
+
251
+ 1. **Parse** - `parseHashdUrl()` extracts CID and metadata from URL
252
+ 2. **Fetch** - `fetchHashdContent()` retrieves data via P2P from ByteCave network
253
+ 3. **Cache** - Blob URLs are automatically cached (1 hour TTL)
254
+ 4. **Reuse** - Subsequent requests for same CID return cached blob URL
255
+
256
+ ### React Hooks
257
+
258
+ For React apps, use hooks instead of manual fetching:
259
+
260
+ ```typescript
261
+ import { useHashdUrl } from '@gethashd/bytecave-browser';
262
+
263
+ function ImageDisplay({ cid }) {
264
+ const { blobUrl, loading, error } = useHashdUrl(`hashd://${cid}`);
265
+
266
+ if (loading) return <Spinner />;
267
+ if (error) return <Error message={error} />;
268
+ return <img src={blobUrl} alt="Image" />;
269
+ }
270
+ ```
271
+
272
+ ## React Integration
273
+
274
+ ### Provider Setup (Recommended)
275
+
276
+ The easiest way to use ByteCave in React is with the `ByteCaveProvider`:
277
+
278
+ ```typescript
279
+ import { ByteCaveProvider } from '@gethashd/bytecave-browser';
280
+
281
+ function App() {
282
+ return (
283
+ <ByteCaveProvider
284
+ vaultNodeRegistryAddress={process.env.REACT_APP_VAULT_REGISTRY}
285
+ rpcUrl={process.env.REACT_APP_RPC_URL}
286
+ relayPeers={process.env.REACT_APP_RELAY_PEERS?.split(',')}
287
+ >
288
+ <YourApp />
289
+ </ByteCaveProvider>
290
+ );
291
+ }
292
+ ```
293
+
294
+ Then use hooks anywhere in your app:
295
+
296
+ ```typescript
297
+ import { useByteCaveContext, useHashdUrl } from '@gethashd/bytecave-browser';
298
+
299
+ function ImageGallery() {
300
+ const { store, retrieve, isConnected } = useByteCaveContext();
301
+
302
+ // Display image from hashd:// URL
303
+ const { blobUrl, loading, error } = useHashdUrl('hashd://abc123...');
304
+
305
+ if (loading) return <div>Loading...</div>;
306
+ if (error) return <div>Error: {error}</div>;
307
+
308
+ return <img src={blobUrl} alt="Stored image" />;
309
+ }
310
+
311
+ function Uploader() {
312
+ const { store, isConnected } = useByteCaveContext();
313
+
314
+ const handleUpload = async (file: File) => {
315
+ const data = new Uint8Array(await file.arrayBuffer());
316
+ const result = await store(data, file.type);
317
+ console.log('Uploaded with CID:', result.cid);
318
+ };
319
+
320
+ return (
321
+ <input
322
+ type="file"
323
+ onChange={(e) => handleUpload(e.target.files[0])}
324
+ disabled={!isConnected}
325
+ />
326
+ );
327
+ }
328
+ ```
329
+
330
+ ### Available Hooks
331
+
332
+ **`useByteCaveContext()`** - Access ByteCave client from context
333
+
334
+ Must be used within `ByteCaveProvider`. Returns:
335
+ ```typescript
336
+ {
337
+ connectionState: 'disconnected' | 'connecting' | 'connected' | 'error',
338
+ peers: PeerInfo[],
339
+ isConnected: boolean,
340
+ connect: () => Promise<void>,
341
+ disconnect: () => Promise<void>,
342
+ store: (data: Uint8Array, contentType?: string, signer?: any) => Promise<StoreResult>,
343
+ retrieve: (cid: string) => Promise<RetrieveResult>,
344
+ getNodeHealth: (peerId: string) => Promise<NodeHealth | null>,
345
+ error: string | null
346
+ }
347
+ ```
348
+
349
+ **`useHashdUrl(url)`** - Convert hashd:// URL to blob URL
350
+
351
+ ```typescript
352
+ function ImageDisplay({ cid }) {
353
+ const { blobUrl, loading, error } = useHashdUrl(`hashd://${cid}`);
354
+
355
+ if (loading) return <Spinner />;
356
+ if (error) return <Error message={error} />;
357
+
358
+ return <img src={blobUrl} alt="Image" />;
359
+ }
360
+ ```
361
+
362
+ **`useHashdImage(url, options)`** - Image-specific loader
363
+
364
+ ```typescript
365
+ function ProfilePicture({ cid }) {
366
+ const { src, loading, error } = useHashdImage(`hashd://${cid}`, {
367
+ client,
368
+ placeholder: '/default-avatar.png'
369
+ });
370
+
371
+ if (loading) return <Spinner />;
372
+ if (error) return <ErrorIcon />;
373
+
374
+ return <img src={src} alt="Profile" className="w-32 h-32 rounded-full" />;
375
+ }
376
+ ```
377
+
378
+ **`useHashdContent(url, options)`** - Generic content loader
379
+
380
+ ```typescript
381
+ function ContentViewer({ url }) {
382
+ const { blobUrl, loading, error, mimeType } = useHashdContent(url, { client });
383
+
384
+ if (loading) return <Spinner />;
385
+ if (error) return <Error message={error.message} />;
386
+
387
+ if (mimeType?.startsWith('image/')) {
388
+ return <img src={blobUrl} />;
389
+ } else if (mimeType?.startsWith('video/')) {
390
+ return <video src={blobUrl} controls />;
391
+ }
392
+
393
+ return <a href={blobUrl} download>Download</a>;
394
+ }
395
+ ```
396
+
397
+ **`useHashdMedia(url, options)`** - Video/audio loader
398
+
399
+ Returns: `{ src, blobUrl, loading, error, cached, refetch }`
400
+
401
+ **`useHashdBatch(urls, options)`** - Batch load multiple URLs
402
+
403
+ ```typescript
404
+ function Gallery({ cids }) {
405
+ const urls = cids.map(cid => `hashd://${cid}`);
406
+ const { results, loading, errors } = useHashdBatch(urls, { client });
407
+
408
+ if (loading) return <Spinner />;
409
+
410
+ return (
411
+ <div className="grid grid-cols-3 gap-4">
412
+ {Array.from(results.entries()).map(([url, result]) => (
413
+ <img key={url} src={result.blobUrl} alt="" />
414
+ ))}
415
+ </div>
416
+ );
417
+ }
418
+ ```
419
+
420
+ ### Components
421
+
422
+ Drop-in replacements for standard HTML elements:
423
+
424
+ ```typescript
425
+ import { HashdImage, HashdVideo, HashdAudio } from '@gethashd/bytecave-browser/react';
426
+
427
+ // Image
428
+ <HashdImage
429
+ src="hashd://abc123..."
430
+ client={byteCaveClient}
431
+ alt="Profile picture"
432
+ className="w-32 h-32 rounded-full"
433
+ placeholder="/loading.png"
434
+ loadingComponent={<Spinner />}
435
+ errorComponent={<ErrorIcon />}
436
+ />
437
+
438
+ // Video
439
+ <HashdVideo
440
+ src="hashd://def456..."
441
+ client={byteCaveClient}
442
+ controls
443
+ className="w-full"
444
+ />
445
+
446
+ // Audio
447
+ <HashdAudio
448
+ src="hashd://ghi789..."
449
+ client={byteCaveClient}
450
+ controls
451
+ />
452
+
453
+ // Render prop pattern
454
+ <HashdContent url="hashd://abc123..." client={client}>
455
+ {({ blobUrl, loading, error }) => {
456
+ if (loading) return <Spinner />;
457
+ if (error) return <Error />;
458
+ return <img src={blobUrl} />;
459
+ }}
460
+ </HashdContent>
461
+ ```
462
+
463
+
464
+ ### Complete Example with Provider
465
+
466
+ ```typescript
467
+ import { ByteCaveProvider, useByteCaveContext, useHashdUrl } from '@gethashd/bytecave-browser';
468
+
469
+ // Wrap your app with the provider
470
+ function App() {
471
+ return (
472
+ <ByteCaveProvider
473
+ vaultNodeRegistryAddress={process.env.REACT_APP_VAULT_REGISTRY}
474
+ rpcUrl={process.env.REACT_APP_RPC_URL}
475
+ relayPeers={process.env.REACT_APP_RELAY_PEERS?.split(',')}
476
+ >
477
+ <Gallery />
478
+ </ByteCaveProvider>
479
+ );
480
+ }
481
+
482
+ // Use hooks in any component
483
+ function Gallery() {
484
+ const { isConnected, store } = useByteCaveContext();
485
+ const { blobUrl, loading, error } = useHashdUrl(
486
+ 'hashd://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi'
487
+ );
488
+
489
+ const handleUpload = async (file: File) => {
490
+ const data = new Uint8Array(await file.arrayBuffer());
491
+ const result = await store(data, file.type);
492
+ console.log('Uploaded:', result.cid);
493
+ };
494
+
495
+ if (!isConnected) return <div>Connecting to ByteCave...</div>;
496
+
497
+ return (
498
+ <div>
499
+ <h1>ByteCave Gallery</h1>
500
+
501
+ {/* Display image */}
502
+ {loading && <div>Loading image...</div>}
503
+ {error && <div>Error: {error}</div>}
504
+ {blobUrl && <img src={blobUrl} alt="Decentralized" className="max-w-md" />}
505
+
506
+ {/* Upload new image */}
507
+ <input type="file" onChange={(e) => handleUpload(e.target.files[0])} />
508
+ </div>
509
+ );
510
+ }
511
+ ```
512
+
513
+ ### Complete Example without Provider
514
+
515
+ If you prefer manual client management:
516
+
517
+ ```typescript
518
+ import { ByteCaveClient } from '@gethashd/bytecave-browser';
519
+ import { HashdImage } from '@gethashd/bytecave-browser';
520
+ import { useState, useEffect } from 'react';
521
+
522
+ function App() {
523
+ const [client, setClient] = useState(null);
524
+
525
+ useEffect(() => {
526
+ const bytecave = new ByteCaveClient({
527
+ relayPeers: process.env.REACT_APP_RELAY_PEERS?.split(','),
528
+ vaultNodeRegistryAddress: process.env.REACT_APP_VAULT_REGISTRY,
529
+ rpcUrl: process.env.REACT_APP_RPC_URL
530
+ });
531
+
532
+ bytecave.start().then(() => setClient(bytecave));
533
+
534
+ return () => bytecave.stop();
535
+ }, []);
536
+
537
+ if (!client) return <div>Connecting to ByteCave...</div>;
538
+
539
+ return (
540
+ <div>
541
+ <h1>ByteCave Gallery</h1>
542
+ <HashdImage
543
+ src="hashd://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"
544
+ client={client}
545
+ alt="Decentralized image"
546
+ className="max-w-md"
547
+ />
548
+ </div>
549
+ );
550
+ }
551
+ ```
552
+
553
+ ## Environment Variables
554
+
555
+ For React apps, configure relay peers via environment:
556
+
557
+ ```bash
558
+ # .env
559
+ REACT_APP_RELAY_PEERS=/dns4/relay.example.com/tcp/4002/ws/p2p/12D3KooW...
560
+ ```
561
+
562
+ Then in your app:
563
+
564
+ ```typescript
565
+ const relayPeers = process.env.REACT_APP_RELAY_PEERS?.split(',') || [];
566
+
567
+ const client = new ByteCaveClient({
568
+ contractAddress: process.env.REACT_APP_VAULT_REGISTRY,
569
+ rpcUrl: process.env.REACT_APP_RPC_URL,
570
+ relayPeers
571
+ });
572
+ ```
573
+
574
+ ## How It Works
575
+
576
+ ### Fast Discovery via Peer Directory
577
+
578
+ On startup, the browser queries the relay's peer directory protocol for instant node discovery:
579
+
580
+ 1. **Connect to Relay** - Browser connects to relay via WebSocket
581
+ 2. **Query Peer Directory** - Browser dials `/bytecave/relay/peers/1.0.0` protocol
582
+ 3. **Receive Peer List** - Relay responds with list of storage nodes and circuit relay addresses
583
+ 4. **Dial Nodes** - Browser dials each node through the relay
584
+ 5. **Fetch Health Data** - Immediate health check via P2P protocol
585
+
586
+ **Discovery time: 1-2 seconds** (down from 2+ minutes with gossip-only)
587
+
588
+ ### Pure P2P Discovery
589
+
590
+ 1. **Connect to Relay** - Browser connects to relay via WebSocket
591
+ 2. **Announce Presence** - Node announces on FloodSub `bytecave-announce` topic
592
+ 3. **Discover Peers** - DHT and pubsub discover other nodes through relay
593
+ 4. **Establish Connection** - Direct WebRTC or relayed connection
594
+ 5. **Store/Retrieve** - P2P protocols for data operations
595
+
596
+ ### Pure P2P Architecture
597
+
598
+ - ✅ No HTTP health endpoint calls
599
+ - ✅ No HTTP multiaddr fetching
600
+ - ✅ Pure libp2p protocols (Peer Directory, FloodSub, DHT)
601
+ - ✅ Works entirely over P2P network
602
+ - ✅ Browser connects directly to storage nodes via WebRTC or relay
603
+
604
+ ## Protocols
605
+
606
+ - **Peer Directory** (`/bytecave/relay/peers/1.0.0`) - Fast node discovery
607
+ - **Health Protocol** (`/bytecave/health/1.0.0`) - Node health checks
608
+ - **WebSocket** - Browser to relay connection
609
+ - **WebRTC** - Direct browser-to-node connections
610
+ - **Circuit Relay v2** - NAT traversal
611
+ - **FloodSub** - Peer announcements
612
+ - **DHT** (via relay) - Distributed peer routing
613
+
614
+ ## Browser Compatibility
615
+
616
+ - Chrome/Edge 89+
617
+ - Firefox 87+
618
+ - Safari 15.4+
619
+ - Opera 75+
620
+
621
+ Requires WebRTC and WebSocket support.
622
+
623
+ ## Troubleshooting
624
+
625
+ ### No Peers Discovered
626
+
627
+ **Check relay configuration:**
628
+ ```typescript
629
+ console.log('Relay peers:', config.relayPeers);
630
+ ```
631
+
632
+ **Verify relay is running:**
633
+ ```bash
634
+ curl http://relay.example.com:4002
635
+ ```
636
+
637
+ **Check browser console:**
638
+ ```
639
+ [ByteCave] Using relay peers: [...]
640
+ [ByteCave] ✓ Connected to relay: ...
641
+ ```
642
+
643
+ ### Connection Failed
644
+
645
+ 1. Verify relay multiaddr format includes `/ws/`
646
+ 2. Check firewall allows WebSocket connections
647
+ 3. Ensure relay peer ID matches running relay
648
+ 4. Try connecting to relay directly in browser DevTools
649
+
650
+ ### CORS Issues
651
+
652
+ WebSocket connections don't have CORS restrictions. If you see CORS errors, you may be using HTTP instead of WS.
653
+
654
+ ## Development
655
+
656
+ ```bash
657
+ # Install dependencies
658
+ yarn install
659
+
660
+ # Build
661
+ yarn build
662
+
663
+ # Run tests
664
+ yarn test
665
+
666
+ # Watch mode
667
+ yarn dev
668
+ ```
669
+
670
+ ## TypeScript Support
671
+
672
+ Full TypeScript definitions included:
673
+
674
+ ```typescript
675
+ import type {
676
+ ByteCaveConfig,
677
+ PeerInfo,
678
+ StoreResult,
679
+ RetrieveResult,
680
+ ConnectionState,
681
+ HashdUrl,
682
+ FetchOptions,
683
+ FetchResult
684
+ } from '@gethashd/bytecave-browser';
685
+ ```
686
+
687
+ ## License
688
+
689
+ MIT
690
+
691
+ ## Related Packages
692
+
693
+ - **bytecave-core** - Storage node implementation
694
+ - **bytecave-relay** - Relay node for NAT traversal
695
+ - **bytecave-desktop** - Desktop application
696
+
697
+ ## Support
698
+
699
+ For issues and questions, please open an issue on GitHub.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Tests for ByteCave Browser P2P Protocol Client (Phase 51)
3
+ *
4
+ * Covers:
5
+ * - P2P protocol client initialization
6
+ * - Store/retrieve via P2P streams
7
+ * - Node info/health retrieval
8
+ * - Base64 encoding/decoding utilities
9
+ */
10
+ export {};