pluto-rtc 0.0.2
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 +84 -0
- package/dist/Connection.d.ts +35 -0
- package/dist/Connection.js +146 -0
- package/dist/ConnectionManager.d.ts +38 -0
- package/dist/ConnectionManager.js +78 -0
- package/dist/api/MediaTransport.d.ts +7 -0
- package/dist/api/MediaTransport.js +262 -0
- package/dist/api/PlutoPeerConnection.d.ts +38 -0
- package/dist/api/PlutoPeerConnection.js +242 -0
- package/dist/api/PlutoWebSocket.d.ts +24 -0
- package/dist/api/PlutoWebSocket.js +112 -0
- package/dist/api/PlutoWebTransport.d.ts +28 -0
- package/dist/api/PlutoWebTransport.js +88 -0
- package/dist/core/Client.d.ts +70 -0
- package/dist/core/Client.js +326 -0
- package/dist/core/Connection.d.ts +66 -0
- package/dist/core/Connection.js +392 -0
- package/dist/core/Room.d.ts +29 -0
- package/dist/core/Room.js +297 -0
- package/dist/core/Signaling.d.ts +37 -0
- package/dist/core/Signaling.js +199 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/react/index.d.ts +9 -0
- package/dist/react/index.js +34 -0
- package/package.json +47 -0
- package/wasm/pkg/README.md +218 -0
- package/wasm/pkg/iroh_wasm.d.ts +148 -0
- package/wasm/pkg/iroh_wasm.js +1382 -0
- package/wasm/pkg/iroh_wasm_bg.wasm +0 -0
- package/wasm/pkg/iroh_wasm_bg.wasm.d.ts +58 -0
- package/wasm/pkg/package.json +15 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# iroh-wasm
|
|
2
|
+
|
|
3
|
+
WASM bindings for [iroh](https://iroh.computer) that enable P2P networking in web browsers.
|
|
4
|
+
|
|
5
|
+
This module compiles the iroh Rust library to WebAssembly, allowing browsers to participate in P2P file sharing without a backend server.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Browser (Web App)
|
|
11
|
+
↓
|
|
12
|
+
WasmIrohConnection.ts (TypeScript)
|
|
13
|
+
↓
|
|
14
|
+
WASM Module (this crate)
|
|
15
|
+
↓
|
|
16
|
+
iroh crate (Rust networking)
|
|
17
|
+
↓
|
|
18
|
+
WebTransport (browser API)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- **In-browser P2P**: No backend required for web clients
|
|
24
|
+
- **QUIC over WebTransport**: Modern protocol for efficient data transfer
|
|
25
|
+
- **Streaming API**: ReadableStream/WritableStream for large file transfers
|
|
26
|
+
- **Node persistence** (future): Use IndexedDB for persistent identity
|
|
27
|
+
|
|
28
|
+
## Build
|
|
29
|
+
|
|
30
|
+
### Prerequisites
|
|
31
|
+
- Rust toolchain
|
|
32
|
+
- `wasm-pack` installed: `cargo install wasm-pack`
|
|
33
|
+
- LLVM/Clang (for linking)
|
|
34
|
+
|
|
35
|
+
### Build Command
|
|
36
|
+
|
|
37
|
+
From the project root:
|
|
38
|
+
```bash
|
|
39
|
+
npm run build:wasm
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or manually:
|
|
43
|
+
```bash
|
|
44
|
+
cd src-tauri/iroh-wasm
|
|
45
|
+
wasm-pack build --target web --out-dir ../../src/services/iroh/wasm/pkg
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This generates:
|
|
49
|
+
- `pkg/iroh_wasm_bg.wasm` - Compiled WebAssembly binary
|
|
50
|
+
- `pkg/iroh_wasm.js` - JavaScript bindings
|
|
51
|
+
- `pkg/iroh_wasm.d.ts` - TypeScript type definitions
|
|
52
|
+
|
|
53
|
+
### Output Location
|
|
54
|
+
|
|
55
|
+
The build outputs to `src/services/iroh/wasm/pkg/` (git-ignored) so the frontend can import it:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import init, { IrohNode } from './wasm/pkg/iroh_wasm';
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Source Structure
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
src/
|
|
65
|
+
├── lib.rs # WASM entry point, exports
|
|
66
|
+
├── node.rs # IrohNode implementation
|
|
67
|
+
└── stream.rs # BiStream wrapper for QUIC streams
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### lib.rs
|
|
71
|
+
- Initializes wasm-bindgen
|
|
72
|
+
- Exports `IrohNode` and `BiStream` types
|
|
73
|
+
- Sets up panic hooks for debugging
|
|
74
|
+
|
|
75
|
+
### node.rs
|
|
76
|
+
**Type**: `IrohNode`
|
|
77
|
+
|
|
78
|
+
Main WASM-exposed class that manages the iroh node instance.
|
|
79
|
+
|
|
80
|
+
**Methods**:
|
|
81
|
+
```rust
|
|
82
|
+
impl IrohNode {
|
|
83
|
+
// Create new node (in-memory for now)
|
|
84
|
+
#[wasm_bindgen(constructor)]
|
|
85
|
+
pub async fn new() -> Result<IrohNode, JsValue>;
|
|
86
|
+
|
|
87
|
+
// Get local node ID
|
|
88
|
+
#[wasm_bindgen]
|
|
89
|
+
pub fn node_id(&self) -> String;
|
|
90
|
+
|
|
91
|
+
// Connect to remote peer
|
|
92
|
+
#[wasm_bindgen]
|
|
93
|
+
pub async fn connect(&self, endpoint_addr: String)
|
|
94
|
+
-> Result<Connection, JsValue>;
|
|
95
|
+
|
|
96
|
+
// Accept incoming connection
|
|
97
|
+
#[wasm_bindgen]
|
|
98
|
+
pub async fn accept(&self) -> Result<Connection, JsValue>;
|
|
99
|
+
|
|
100
|
+
// Open bidirectional stream
|
|
101
|
+
#[wasm_bindgen]
|
|
102
|
+
pub async fn open_bi(&self) -> Result<BiStream, JsValue>;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### stream.rs
|
|
107
|
+
**Type**: `BiStream`
|
|
108
|
+
|
|
109
|
+
Wraps iroh's bidirectional QUIC streams in browser-compatible streams.
|
|
110
|
+
|
|
111
|
+
**Returns**:
|
|
112
|
+
- `ReadableStream<Uint8Array>` - For receiving data
|
|
113
|
+
- `WritableStream<Uint8Array>` - For sending data
|
|
114
|
+
|
|
115
|
+
**Usage from TypeScript**:
|
|
116
|
+
```typescript
|
|
117
|
+
const stream = await connection.open_bi();
|
|
118
|
+
const reader = stream.readable.getReader();
|
|
119
|
+
const writer = stream.writable.getWriter();
|
|
120
|
+
|
|
121
|
+
// Send data
|
|
122
|
+
await writer.write(new Uint8Array([1, 2, 3]));
|
|
123
|
+
|
|
124
|
+
// Receive data
|
|
125
|
+
const { value, done } = await reader.read();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Integration with Frontend
|
|
129
|
+
|
|
130
|
+
### WasmIrohConnection.ts
|
|
131
|
+
|
|
132
|
+
The TypeScript wrapper (`src/services/iroh/WasmIrohConnection.ts`) uses this WASM module:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import init, { IrohNode } from './wasm/pkg/iroh_wasm';
|
|
136
|
+
|
|
137
|
+
// Lazy load WASM
|
|
138
|
+
await init();
|
|
139
|
+
|
|
140
|
+
// Create singleton node
|
|
141
|
+
const node = await IrohNode.new();
|
|
142
|
+
|
|
143
|
+
// Get node ID
|
|
144
|
+
const nodeId = node.node_id();
|
|
145
|
+
|
|
146
|
+
// Connect to peer
|
|
147
|
+
const connection = await node.connect(peerEndpointAddr);
|
|
148
|
+
|
|
149
|
+
// Open stream
|
|
150
|
+
const biStream = await connection.open_bi();
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Platform Differences
|
|
154
|
+
|
|
155
|
+
| Feature | Desktop/Mobile (Tauri) | Web (WASM) |
|
|
156
|
+
|---------|------------------------|------------|
|
|
157
|
+
| Backend | Rust via Tauri commands | Direct WASM calls |
|
|
158
|
+
| Storage | Persistent (filesystem) | In-memory |
|
|
159
|
+
| Transport | QUIC over UDP | QUIC over WebTransport |
|
|
160
|
+
| Performance | Native speed | Near-native |
|
|
161
|
+
|
|
162
|
+
## Limitations
|
|
163
|
+
|
|
164
|
+
### Current
|
|
165
|
+
- **No persistence**: Node identity resets on page reload
|
|
166
|
+
- **Memory-only**: No disk storage (by design for security)
|
|
167
|
+
- **Single node**: One node per browser tab
|
|
168
|
+
|
|
169
|
+
### Future Improvements
|
|
170
|
+
1. **IndexedDB storage**: Persist node identity across sessions
|
|
171
|
+
2. **Service Worker**: Background P2P even when page closed
|
|
172
|
+
3. **Better error handling**: More descriptive errors from Rust→JS
|
|
173
|
+
4. **Connection pooling**: Reuse connections across components
|
|
174
|
+
|
|
175
|
+
## Debugging
|
|
176
|
+
|
|
177
|
+
### Enable WASM logs:
|
|
178
|
+
```typescript
|
|
179
|
+
// In browser console
|
|
180
|
+
localStorage.setItem('debug', 'iroh:*');
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Common Issues
|
|
184
|
+
|
|
185
|
+
**"Memory access out of bounds"**
|
|
186
|
+
- WASM memory exhausted
|
|
187
|
+
- Try reducing concurrent connections/streams
|
|
188
|
+
|
|
189
|
+
**"WebTransport not supported"**
|
|
190
|
+
- Browser doesn't support WebTransport
|
|
191
|
+
- Requires Chrome 97+, Edge 97+, or Opera 83+
|
|
192
|
+
|
|
193
|
+
**"Module not found"**
|
|
194
|
+
- WASM not built or path incorrect
|
|
195
|
+
- Run `npm run build:wasm`
|
|
196
|
+
- Check `src/services/iroh/wasm/pkg/` exists
|
|
197
|
+
|
|
198
|
+
## Dependencies
|
|
199
|
+
|
|
200
|
+
Key Rust crates:
|
|
201
|
+
- `iroh` - P2P networking
|
|
202
|
+
- `wasm-bindgen` - Rust↔JavaScript bindings
|
|
203
|
+
- `web-sys` - Browser API access
|
|
204
|
+
- `js-sys` - JavaScript type system
|
|
205
|
+
|
|
206
|
+
## Performance
|
|
207
|
+
|
|
208
|
+
- **Build time**: ~60 seconds (release mode)
|
|
209
|
+
- **WASM size**: ~2MB (gzipped: ~600KB)
|
|
210
|
+
- **Startup time**: ~100ms (first load)
|
|
211
|
+
- **Memory**: ~5MB per node
|
|
212
|
+
|
|
213
|
+
## See Also
|
|
214
|
+
|
|
215
|
+
- [Complete Iroh Architecture](../../docs/IROH_ARCHITECTURE.md) - Full system documentation
|
|
216
|
+
- [Frontend README](../../src/services/iroh/README.md) - TypeScript integration
|
|
217
|
+
- [Backend README](../../src-tauri/src/iroh/README.md) - Rust implementation
|
|
218
|
+
- [wasm-bindgen Book](https://rustwasm.github.io/wasm-bindgen/) - WASM bindings guide
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/**
|
|
4
|
+
* The `ReadableStreamType` enum.
|
|
5
|
+
*
|
|
6
|
+
* *This API requires the following crate features to be activated: `ReadableStreamType`*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
type ReadableStreamType = "bytes";
|
|
10
|
+
|
|
11
|
+
export class BiStream {
|
|
12
|
+
private constructor();
|
|
13
|
+
free(): void;
|
|
14
|
+
[Symbol.dispose](): void;
|
|
15
|
+
readonly endpoint_id: string;
|
|
16
|
+
readonly recv: ReadableStream;
|
|
17
|
+
readonly send: WritableStream;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class IntoUnderlyingByteSource {
|
|
21
|
+
private constructor();
|
|
22
|
+
free(): void;
|
|
23
|
+
[Symbol.dispose](): void;
|
|
24
|
+
cancel(): void;
|
|
25
|
+
pull(controller: ReadableByteStreamController): Promise<any>;
|
|
26
|
+
start(controller: ReadableByteStreamController): void;
|
|
27
|
+
readonly autoAllocateChunkSize: number;
|
|
28
|
+
readonly type: ReadableStreamType;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class IntoUnderlyingSink {
|
|
32
|
+
private constructor();
|
|
33
|
+
free(): void;
|
|
34
|
+
[Symbol.dispose](): void;
|
|
35
|
+
abort(reason: any): Promise<any>;
|
|
36
|
+
close(): Promise<any>;
|
|
37
|
+
write(chunk: any): Promise<any>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class IntoUnderlyingSource {
|
|
41
|
+
private constructor();
|
|
42
|
+
free(): void;
|
|
43
|
+
[Symbol.dispose](): void;
|
|
44
|
+
cancel(): void;
|
|
45
|
+
pull(controller: ReadableStreamDefaultController): Promise<any>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class IrohNode {
|
|
49
|
+
private constructor();
|
|
50
|
+
free(): void;
|
|
51
|
+
[Symbol.dispose](): void;
|
|
52
|
+
addNodeAddr(node_id: string, relay_url: string | null | undefined, direct_addresses: string[]): Promise<void>;
|
|
53
|
+
connect(endpoint_id: string): ReadableStream;
|
|
54
|
+
disconnect(endpoint_id: string): Promise<void>;
|
|
55
|
+
events(): ReadableStream;
|
|
56
|
+
incoming_streams(): ReadableStream;
|
|
57
|
+
node_addr(): Promise<string>;
|
|
58
|
+
open_bi(endpoint_id: string): Promise<BiStream>;
|
|
59
|
+
open_uni(endpoint_id: string): Promise<WritableStream>;
|
|
60
|
+
static spawn(secret_key?: Uint8Array | null): Promise<IrohNode>;
|
|
61
|
+
readonly endpoint_id: string;
|
|
62
|
+
readonly secret_key: Uint8Array;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function start(): void;
|
|
66
|
+
|
|
67
|
+
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
68
|
+
|
|
69
|
+
export interface InitOutput {
|
|
70
|
+
readonly memory: WebAssembly.Memory;
|
|
71
|
+
readonly start: () => void;
|
|
72
|
+
readonly __wbg_bistream_free: (a: number, b: number) => void;
|
|
73
|
+
readonly bistream_recv: (a: number) => any;
|
|
74
|
+
readonly bistream_send: (a: number) => any;
|
|
75
|
+
readonly bistream_endpoint_id: (a: number) => [number, number];
|
|
76
|
+
readonly __wbg_irohnode_free: (a: number, b: number) => void;
|
|
77
|
+
readonly irohnode_spawn: (a: number, b: number) => any;
|
|
78
|
+
readonly irohnode_events: (a: number) => any;
|
|
79
|
+
readonly irohnode_incoming_streams: (a: number) => any;
|
|
80
|
+
readonly irohnode_addNodeAddr: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => any;
|
|
81
|
+
readonly irohnode_endpoint_id: (a: number) => [number, number];
|
|
82
|
+
readonly irohnode_secret_key: (a: number) => [number, number];
|
|
83
|
+
readonly irohnode_node_addr: (a: number) => any;
|
|
84
|
+
readonly irohnode_connect: (a: number, b: number, c: number) => [number, number, number];
|
|
85
|
+
readonly irohnode_disconnect: (a: number, b: number, c: number) => any;
|
|
86
|
+
readonly irohnode_open_bi: (a: number, b: number, c: number) => any;
|
|
87
|
+
readonly irohnode_open_uni: (a: number, b: number, c: number) => any;
|
|
88
|
+
readonly __wbg_intounderlyingsource_free: (a: number, b: number) => void;
|
|
89
|
+
readonly intounderlyingsource_pull: (a: number, b: any) => any;
|
|
90
|
+
readonly intounderlyingsource_cancel: (a: number) => void;
|
|
91
|
+
readonly __wbg_intounderlyingsink_free: (a: number, b: number) => void;
|
|
92
|
+
readonly intounderlyingsink_write: (a: number, b: any) => any;
|
|
93
|
+
readonly intounderlyingsink_close: (a: number) => any;
|
|
94
|
+
readonly intounderlyingsink_abort: (a: number, b: any) => any;
|
|
95
|
+
readonly __wbg_intounderlyingbytesource_free: (a: number, b: number) => void;
|
|
96
|
+
readonly intounderlyingbytesource_type: (a: number) => number;
|
|
97
|
+
readonly intounderlyingbytesource_autoAllocateChunkSize: (a: number) => number;
|
|
98
|
+
readonly intounderlyingbytesource_start: (a: number, b: any) => void;
|
|
99
|
+
readonly intounderlyingbytesource_pull: (a: number, b: any) => any;
|
|
100
|
+
readonly intounderlyingbytesource_cancel: (a: number) => void;
|
|
101
|
+
readonly ring_core_0_17_14__bn_mul_mont: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
|
|
102
|
+
readonly wasm_bindgen__closure__destroy__h7cf29a7e95a6517c: (a: number, b: number) => void;
|
|
103
|
+
readonly wasm_bindgen__closure__destroy__heb08e36b77a85b9c: (a: number, b: number) => void;
|
|
104
|
+
readonly wasm_bindgen__closure__destroy__h18332ba013e398aa: (a: number, b: number) => void;
|
|
105
|
+
readonly wasm_bindgen__closure__destroy__h87cf50f4bd51ca2e: (a: number, b: number) => void;
|
|
106
|
+
readonly wasm_bindgen__closure__destroy__h920903f13ccee978: (a: number, b: number) => void;
|
|
107
|
+
readonly wasm_bindgen__closure__destroy__h5ecb98b48ddb9e08: (a: number, b: number) => void;
|
|
108
|
+
readonly wasm_bindgen__closure__destroy__h5c4e7138d6491581: (a: number, b: number) => void;
|
|
109
|
+
readonly wasm_bindgen__convert__closures_____invoke__h39b19e5fabe12541: (a: number, b: number, c: any, d: any) => void;
|
|
110
|
+
readonly wasm_bindgen__convert__closures_____invoke__h993a01742f0967bf: (a: number, b: number, c: any) => void;
|
|
111
|
+
readonly wasm_bindgen__convert__closures_____invoke__hb5204d4e41ff3977: (a: number, b: number, c: any) => void;
|
|
112
|
+
readonly wasm_bindgen__convert__closures_____invoke__h650bb17850b2b136: (a: number, b: number, c: any) => void;
|
|
113
|
+
readonly wasm_bindgen__convert__closures_____invoke__h36c5adc97d437817: (a: number, b: number) => void;
|
|
114
|
+
readonly wasm_bindgen__convert__closures_____invoke__hb17976c66e1a6d68: (a: number, b: number) => void;
|
|
115
|
+
readonly wasm_bindgen__convert__closures_____invoke__h81b4c013358ef500: (a: number, b: number) => void;
|
|
116
|
+
readonly wasm_bindgen__convert__closures_____invoke__h19f413c72615257c: (a: number, b: number) => void;
|
|
117
|
+
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
|
118
|
+
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
119
|
+
readonly __wbindgen_exn_store: (a: number) => void;
|
|
120
|
+
readonly __externref_table_alloc: () => number;
|
|
121
|
+
readonly __wbindgen_externrefs: WebAssembly.Table;
|
|
122
|
+
readonly __externref_drop_slice: (a: number, b: number) => void;
|
|
123
|
+
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
124
|
+
readonly __externref_table_dealloc: (a: number) => void;
|
|
125
|
+
readonly __wbindgen_start: () => void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Instantiates the given `module`, which can either be bytes or
|
|
132
|
+
* a precompiled `WebAssembly.Module`.
|
|
133
|
+
*
|
|
134
|
+
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
|
|
135
|
+
*
|
|
136
|
+
* @returns {InitOutput}
|
|
137
|
+
*/
|
|
138
|
+
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
|
142
|
+
* for everything else, calls `WebAssembly.instantiate` directly.
|
|
143
|
+
*
|
|
144
|
+
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
|
|
145
|
+
*
|
|
146
|
+
* @returns {Promise<InitOutput>}
|
|
147
|
+
*/
|
|
148
|
+
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
|