@freqhole/midden 0.1.30
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 +79 -0
- package/midden.d.ts +388 -0
- package/midden.js +9 -0
- package/midden_bg.js +1830 -0
- package/midden_bg.wasm +0 -0
- package/package.json +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# midden
|
|
2
|
+
|
|
3
|
+
browser WASM client for freqhole P2P federation.
|
|
4
|
+
|
|
5
|
+
## what it does
|
|
6
|
+
|
|
7
|
+
midden provides a `MiddenNode` class that lets browsers connect to freqhole peers over iroh P2P. just need the peer's node_id (public key) to connect - iroh handles discovery via relay.
|
|
8
|
+
|
|
9
|
+
## building
|
|
10
|
+
|
|
11
|
+
requires wasm-pack and LLVM toolchain:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# install wasm-pack
|
|
15
|
+
cargo install wasm-pack
|
|
16
|
+
|
|
17
|
+
# on macOS, ensure LLVM is installed
|
|
18
|
+
brew install llvm
|
|
19
|
+
|
|
20
|
+
# build (dev mode)
|
|
21
|
+
make build
|
|
22
|
+
|
|
23
|
+
# build (release mode, optimized)
|
|
24
|
+
make build-release
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
output goes to `pkg/` directory.
|
|
28
|
+
|
|
29
|
+
## usage
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { MiddenNode } from "midden";
|
|
33
|
+
|
|
34
|
+
// create node (waits for relay connection)
|
|
35
|
+
const node = await MiddenNode.create();
|
|
36
|
+
console.log("my node_id:", node.node_id());
|
|
37
|
+
|
|
38
|
+
// make API request to peer - accepts plain node_id or full endpoint JSON
|
|
39
|
+
const response = await node.proxy_request(
|
|
40
|
+
peerNodeId, // e.g. "abc123def456..." or '{"id":"...","addrs":[...]}'
|
|
41
|
+
"GET",
|
|
42
|
+
"/api/music/songs?limit=10",
|
|
43
|
+
null,
|
|
44
|
+
);
|
|
45
|
+
console.log(response.status, response.body);
|
|
46
|
+
|
|
47
|
+
// fetch blob from peer
|
|
48
|
+
const blob = await node.fetch_blob(peerNodeId, blobId);
|
|
49
|
+
console.log(blob.size(), blob.content_type());
|
|
50
|
+
// blob.data() returns Uint8Array
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### peer address formats
|
|
54
|
+
|
|
55
|
+
midden accepts two formats for `peer_addr`:
|
|
56
|
+
|
|
57
|
+
1. **plain node_id** (64 hex chars): uses iroh relay for discovery
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
13a257b5367d6b5b7ceb67ec6246c3dafbe886af8ed429408cd7619c7a4787b1
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
2. **full endpoint JSON**: includes relay URL and/or direct IP hints
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"id": "13a257b5...",
|
|
67
|
+
"addrs": [{ "Relay": "https://..." }, { "Ip": "192.168.1.100:57383" }]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## protocol
|
|
72
|
+
|
|
73
|
+
uses same protocol as grimoire's federation transport:
|
|
74
|
+
|
|
75
|
+
- ALPN: `freqhole/1`
|
|
76
|
+
- messages: `ProxyRequest`, `ProxyResponse`, `BlobStreamRequest`, `BlobStreamResponse`
|
|
77
|
+
- blob streaming: length-prefixed header followed by raw bytes
|
|
78
|
+
|
|
79
|
+
see `grimoire/src/federation/transport/protocol.rs` for details.
|
package/midden.d.ts
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
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
|
+
/**
|
|
12
|
+
* a bidirectional QUIC stream for length-delimited message exchange.
|
|
13
|
+
*
|
|
14
|
+
* wraps an iroh (SendStream, RecvStream) pair. messages are framed with
|
|
15
|
+
* a 4-byte big-endian u32 length prefix, matching `LengthDelimitedCodec`
|
|
16
|
+
* from tokio-util.
|
|
17
|
+
*
|
|
18
|
+
* the send and recv halves use RefCell<Option<...>> so that async read
|
|
19
|
+
* and write operations can proceed concurrently (safe because WASM is
|
|
20
|
+
* single-threaded).
|
|
21
|
+
*/
|
|
22
|
+
export class BiStream {
|
|
23
|
+
private constructor();
|
|
24
|
+
free(): void;
|
|
25
|
+
[Symbol.dispose](): void;
|
|
26
|
+
/**
|
|
27
|
+
* the ALPN protocol this stream was established on.
|
|
28
|
+
*/
|
|
29
|
+
alpn(): string;
|
|
30
|
+
/**
|
|
31
|
+
* close the stream.
|
|
32
|
+
*
|
|
33
|
+
* finishes the send half and drops both halves.
|
|
34
|
+
*/
|
|
35
|
+
close(): void;
|
|
36
|
+
/**
|
|
37
|
+
* the remote peer's node ID (iroh public key as hex string).
|
|
38
|
+
*/
|
|
39
|
+
peer_node_id(): string;
|
|
40
|
+
/**
|
|
41
|
+
* read a newline-terminated utf-8 line.
|
|
42
|
+
*
|
|
43
|
+
* returns the line WITHOUT the trailing `\n`. returns null on clean
|
|
44
|
+
* stream close (EOF before any bytes). used for ndjson framing.
|
|
45
|
+
*/
|
|
46
|
+
read_line(): Promise<any>;
|
|
47
|
+
/**
|
|
48
|
+
* read a length-delimited message.
|
|
49
|
+
*
|
|
50
|
+
* reads a 4-byte big-endian u32 length prefix, then reads that many
|
|
51
|
+
* bytes of payload. returns the payload as a Uint8Array.
|
|
52
|
+
*
|
|
53
|
+
* returns null (JsValue::NULL) if the stream has been closed cleanly
|
|
54
|
+
* by the remote peer (EOF on the length prefix read).
|
|
55
|
+
*/
|
|
56
|
+
read_message(): Promise<any>;
|
|
57
|
+
/**
|
|
58
|
+
* read all remaining bytes from the recv stream (no length prefix).
|
|
59
|
+
*
|
|
60
|
+
* reads until the remote peer finishes the stream or `max_size` bytes
|
|
61
|
+
* are read. this matches grimoire's `read_to_end()` framing where
|
|
62
|
+
* the message is terminated by the sender calling `finish()`.
|
|
63
|
+
*/
|
|
64
|
+
read_to_end(max_size: number): Promise<any>;
|
|
65
|
+
/**
|
|
66
|
+
* write a newline-delimited utf-8 line.
|
|
67
|
+
*
|
|
68
|
+
* appends `\n` if not already present, then writes. used for the ndjson
|
|
69
|
+
* framing the `freqhole-events/1` protocol speaks.
|
|
70
|
+
*/
|
|
71
|
+
write_line(line: string): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* write a length-delimited message.
|
|
74
|
+
*
|
|
75
|
+
* writes a 4-byte big-endian u32 length prefix followed by the payload.
|
|
76
|
+
* this matches the `LengthDelimitedCodec` framing used by the
|
|
77
|
+
* iroh-automerge-repo example.
|
|
78
|
+
*/
|
|
79
|
+
write_message(data: Uint8Array): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* write raw bytes without a length prefix, then finish the send stream.
|
|
82
|
+
*
|
|
83
|
+
* this matches grimoire's `send_response()` framing where the message
|
|
84
|
+
* is terminated by calling `finish()` on the send stream. the receiver
|
|
85
|
+
* uses `read_to_end()` to read all bytes.
|
|
86
|
+
*
|
|
87
|
+
* after `finish()` we await `stopped()` so the peer's ack is observed
|
|
88
|
+
* before this method returns. without this, JS callers that drop /
|
|
89
|
+
* `close()` the stream immediately after `write_raw_and_finish` can
|
|
90
|
+
* race the QUIC flush -- the peer's `read_to_end` then errors with
|
|
91
|
+
* "connection lost" mid-payload because the in-flight frames are
|
|
92
|
+
* torn down with the connection. matters most for large payloads
|
|
93
|
+
* (e.g. base64-encoded blob bodies in `proxy_response`).
|
|
94
|
+
*/
|
|
95
|
+
write_raw_and_finish(data: Uint8Array): Promise<void>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* result from fetching the server hello image from a peer
|
|
100
|
+
*/
|
|
101
|
+
export class HelloImageResult {
|
|
102
|
+
private constructor();
|
|
103
|
+
free(): void;
|
|
104
|
+
[Symbol.dispose](): void;
|
|
105
|
+
readonly content_type: string | undefined;
|
|
106
|
+
readonly data: Uint8Array;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export class IntoUnderlyingByteSource {
|
|
110
|
+
private constructor();
|
|
111
|
+
free(): void;
|
|
112
|
+
[Symbol.dispose](): void;
|
|
113
|
+
cancel(): void;
|
|
114
|
+
pull(controller: ReadableByteStreamController): Promise<any>;
|
|
115
|
+
start(controller: ReadableByteStreamController): void;
|
|
116
|
+
readonly autoAllocateChunkSize: number;
|
|
117
|
+
readonly type: ReadableStreamType;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export class IntoUnderlyingSink {
|
|
121
|
+
private constructor();
|
|
122
|
+
free(): void;
|
|
123
|
+
[Symbol.dispose](): void;
|
|
124
|
+
abort(reason: any): Promise<any>;
|
|
125
|
+
close(): Promise<any>;
|
|
126
|
+
write(chunk: any): Promise<any>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export class IntoUnderlyingSource {
|
|
130
|
+
private constructor();
|
|
131
|
+
free(): void;
|
|
132
|
+
[Symbol.dispose](): void;
|
|
133
|
+
cancel(): void;
|
|
134
|
+
pull(controller: ReadableStreamDefaultController): Promise<any>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* browser P2P node for freqhole federation
|
|
139
|
+
*
|
|
140
|
+
* supports two protocols:
|
|
141
|
+
* - freqhole/1: API proxying and small blob streaming
|
|
142
|
+
* - iroh-blobs: verified streaming for audio files
|
|
143
|
+
*/
|
|
144
|
+
export class MiddenNode {
|
|
145
|
+
private constructor();
|
|
146
|
+
free(): void;
|
|
147
|
+
[Symbol.dispose](): void;
|
|
148
|
+
/**
|
|
149
|
+
* accept the next incoming connection and bidirectional stream.
|
|
150
|
+
*
|
|
151
|
+
* blocks until an incoming connection arrives on any registered ALPN.
|
|
152
|
+
* returns a BiStream with the peer's node ID and the negotiated ALPN.
|
|
153
|
+
*
|
|
154
|
+
* returns null (JsValue::NULL) if the endpoint has been closed.
|
|
155
|
+
*
|
|
156
|
+
* the caller should check `stream.alpn()` to route the connection
|
|
157
|
+
* to the appropriate handler.
|
|
158
|
+
*/
|
|
159
|
+
accept(): Promise<any>;
|
|
160
|
+
/**
|
|
161
|
+
* return the number of blobs currently held in the store via active TempTags.
|
|
162
|
+
*/
|
|
163
|
+
active_blob_count(): number;
|
|
164
|
+
/**
|
|
165
|
+
* compute blake3 hash for a blob on demand
|
|
166
|
+
*
|
|
167
|
+
* use this when the client doesn't have the blake3 hash yet (not in API response).
|
|
168
|
+
* the server will compute the hash, save it to the database, and add the file
|
|
169
|
+
* to FsStore for verified streaming.
|
|
170
|
+
*
|
|
171
|
+
* returns the blake3 hash (64 hex chars) if successful, null if blob not found.
|
|
172
|
+
*/
|
|
173
|
+
compute_blake3(peer_addr: string, blob_id: string): Promise<string | undefined>;
|
|
174
|
+
/**
|
|
175
|
+
* create a new node with random identity
|
|
176
|
+
* waits for relay connection before returning
|
|
177
|
+
*/
|
|
178
|
+
static create(): Promise<MiddenNode>;
|
|
179
|
+
/**
|
|
180
|
+
* create a node from existing secret key bytes (for persistence)
|
|
181
|
+
* key_bytes must be exactly 32 bytes
|
|
182
|
+
*/
|
|
183
|
+
static create_from_key(key_bytes: Uint8Array): Promise<MiddenNode>;
|
|
184
|
+
/**
|
|
185
|
+
* create a node from existing secret key with additional ALPN protocols.
|
|
186
|
+
*
|
|
187
|
+
* `extra_alpns` is a JS array of strings (e.g. ["iroh/automerge-repo/1"]).
|
|
188
|
+
* the node always registers "freqhole/1" plus whatever extra ALPNs are given.
|
|
189
|
+
*/
|
|
190
|
+
static create_with_alpns(key_bytes: Uint8Array, extra_alpns: Array<any>): Promise<MiddenNode>;
|
|
191
|
+
/**
|
|
192
|
+
* download a blob using iroh-blobs verified streaming
|
|
193
|
+
*
|
|
194
|
+
* this is the preferred method for audio files - provides:
|
|
195
|
+
* - verified streaming (each chunk is cryptographically verified)
|
|
196
|
+
* - resume support (can restart interrupted transfers)
|
|
197
|
+
* - efficient parallel chunk fetching
|
|
198
|
+
*
|
|
199
|
+
* peer_addr: plain node_id or full endpoint JSON
|
|
200
|
+
* blake3_hash: the blake3 hash of the blob (64 hex chars)
|
|
201
|
+
*/
|
|
202
|
+
download_verified(peer_addr: string, blake3_hash: string): Promise<Uint8Array>;
|
|
203
|
+
/**
|
|
204
|
+
* download a blob by blob_id using verified streaming with on-demand blake3
|
|
205
|
+
*
|
|
206
|
+
* use this when the client doesn't have the blake3 hash yet (not in API response).
|
|
207
|
+
* computes blake3 on the server, then uses iroh-blobs verified streaming.
|
|
208
|
+
*
|
|
209
|
+
* returns (blob_data, blake3_hash) for caching the hash for future requests.
|
|
210
|
+
*/
|
|
211
|
+
download_verified_by_id(peer_addr: string, blob_id: string): Promise<Array<any>>;
|
|
212
|
+
/**
|
|
213
|
+
* download a verified blob and stream chunks to JS via callback
|
|
214
|
+
*
|
|
215
|
+
* this is the preferred path for large blobs (audio files). instead of
|
|
216
|
+
* materializing the full blob in wasm linear memory (which fails around
|
|
217
|
+
* 32MB+ due to allocator pressure on a single contiguous Bytes), this:
|
|
218
|
+
*
|
|
219
|
+
* 1. downloads the blob into MemStore using the verified iroh-blobs path
|
|
220
|
+
* 2. opens a streaming reader and pulls chunks
|
|
221
|
+
* 3. delivers each chunk to the JS callback as a Uint8Array
|
|
222
|
+
*
|
|
223
|
+
* JS side accumulates chunks (e.g. into a Blob via array of BlobParts) and
|
|
224
|
+
* can release each chunk as it goes. wasm peak memory stays bounded by
|
|
225
|
+
* chunk_size + the original MemStore copy.
|
|
226
|
+
*
|
|
227
|
+
* callback signature: `on_chunk(chunk: Uint8Array, offset: u64) -> void`
|
|
228
|
+
* progress callback: `on_progress(fraction: f64) -> void`
|
|
229
|
+
*
|
|
230
|
+
* returns total bytes streamed.
|
|
231
|
+
*/
|
|
232
|
+
download_verified_streaming(peer_addr: string, blake3_hash: string, total_size: number, on_chunk: Function, on_progress: Function): Promise<number>;
|
|
233
|
+
/**
|
|
234
|
+
* streaming download with auto ensure+retry. first attempts the streaming
|
|
235
|
+
* download; if the verified download fails (blob not in peer's store), calls
|
|
236
|
+
* ensure_blob to load it, then retries.
|
|
237
|
+
*/
|
|
238
|
+
download_verified_streaming_with_ensure(peer_addr: string, blake3_hash: string, total_size: number, on_chunk: Function, on_progress: Function): Promise<number>;
|
|
239
|
+
/**
|
|
240
|
+
* download a blob using iroh-blobs with automatic ensure + retry
|
|
241
|
+
*
|
|
242
|
+
* tries download_verified first. if blob not in peer's FsStore,
|
|
243
|
+
* calls ensure_blob to load it, then retries.
|
|
244
|
+
*/
|
|
245
|
+
download_verified_with_ensure(peer_addr: string, blake3_hash: string): Promise<Uint8Array>;
|
|
246
|
+
/**
|
|
247
|
+
* ensure a blob is loaded into the peer's FsStore by blake3 hash
|
|
248
|
+
*
|
|
249
|
+
* call this before retrying download_verified if the first attempt fails.
|
|
250
|
+
* the server will look up the file by blake3 hash and add it to FsStore.
|
|
251
|
+
*
|
|
252
|
+
* returns true if blob is now available, false if not found.
|
|
253
|
+
*/
|
|
254
|
+
ensure_blob(peer_addr: string, blake3_hash: string): Promise<boolean>;
|
|
255
|
+
/**
|
|
256
|
+
* fetch server image from a peer (public, no auth required)
|
|
257
|
+
* used during "add remote" flow before user is authenticated
|
|
258
|
+
* peer_addr can be plain node_id or full endpoint JSON with relay/IP hints
|
|
259
|
+
*/
|
|
260
|
+
fetch_hello_image(peer_addr: string): Promise<HelloImageResult>;
|
|
261
|
+
/**
|
|
262
|
+
* check whether a blob with the given blake3 hash is currently held in the MemStore
|
|
263
|
+
* via an active TempTag. avoids expensive OPFS read + bao recomputation when the
|
|
264
|
+
* blob is already loaded.
|
|
265
|
+
*/
|
|
266
|
+
has_active_blob(blake3_hash: string): boolean;
|
|
267
|
+
/**
|
|
268
|
+
* import a blob from its pre-computed bao-encoded bytes, skipping the
|
|
269
|
+
* expensive bao tree computation. `blake3_hash` is the 64-char hex hash,
|
|
270
|
+
* `bao_data` is the bao-encoded bytes previously returned by
|
|
271
|
+
* `import_blob_and_export_bao`.
|
|
272
|
+
*
|
|
273
|
+
* uses `import_bao_bytes` (iroh-blobs internal API) to feed the pre-computed
|
|
274
|
+
* bao stream directly into the store, then creates a global TempTag via
|
|
275
|
+
* `Tags::temp_tag` to prevent GC.
|
|
276
|
+
*/
|
|
277
|
+
import_bao(blake3_hash: string, bao_data: Uint8Array): Promise<string>;
|
|
278
|
+
/**
|
|
279
|
+
* import raw bytes into the iroh-blobs store, returning the blake3 hash.
|
|
280
|
+
* this makes the blob available for verified download by peers.
|
|
281
|
+
* the blob stays in the store as long as its TempTag is held in active_tags.
|
|
282
|
+
* call release_blob() to allow GC, or it will be evicted when the map exceeds 3 entries.
|
|
283
|
+
*/
|
|
284
|
+
import_blob(data: Uint8Array): Promise<string>;
|
|
285
|
+
/**
|
|
286
|
+
* import raw bytes into the iroh-blobs store, returning both the blake3 hash
|
|
287
|
+
* AND the bao-encoded bytes. the bao bytes can be cached in OPFS and later
|
|
288
|
+
* fed to `import_bao` to skip the expensive bao tree recomputation on re-import.
|
|
289
|
+
*
|
|
290
|
+
* returns a JS object: `{ hash: string, bao: Uint8Array }`
|
|
291
|
+
*/
|
|
292
|
+
import_blob_and_export_bao(data: Uint8Array): Promise<any>;
|
|
293
|
+
/**
|
|
294
|
+
* get our node_id (iroh public key)
|
|
295
|
+
*/
|
|
296
|
+
node_id(): string;
|
|
297
|
+
/**
|
|
298
|
+
* open a bidirectional stream to a peer on a specific ALPN.
|
|
299
|
+
*
|
|
300
|
+
* `peer_addr` can be a plain node_id hex string or a full endpoint
|
|
301
|
+
* address JSON (same format as proxy_request). `alpn` is the protocol
|
|
302
|
+
* to negotiate (e.g. "iroh/automerge-repo/1").
|
|
303
|
+
*
|
|
304
|
+
* returns a BiStream for length-delimited message exchange.
|
|
305
|
+
*/
|
|
306
|
+
open_bi(peer_addr: string, alpn: string): Promise<BiStream>;
|
|
307
|
+
/**
|
|
308
|
+
* dispatch a typed admin command to a peer over the freqhole-admin/1 ALPN.
|
|
309
|
+
*
|
|
310
|
+
* `args` is a JSON string (the literal `"null"` is accepted for no-payload
|
|
311
|
+
* commands). returns a JS object envelope `{ success, message, data, errors }`
|
|
312
|
+
* matching the wire format. validation of `data` against the per-command
|
|
313
|
+
* schema happens in the spume `AdminClient`.
|
|
314
|
+
*/
|
|
315
|
+
proxy_admin(peer_addr: string, command: string, args: string): Promise<any>;
|
|
316
|
+
/**
|
|
317
|
+
* send an API request to a peer
|
|
318
|
+
* peer_addr can be plain node_id or full endpoint JSON with relay/IP hints
|
|
319
|
+
*/
|
|
320
|
+
proxy_request(peer_addr: string, method: string, path: string, body?: string | null): Promise<any>;
|
|
321
|
+
/**
|
|
322
|
+
* release a blob's TempTag, allowing the store to garbage-collect it.
|
|
323
|
+
* blake3_hash should be the 64-char hex string returned by import_blob.
|
|
324
|
+
*/
|
|
325
|
+
release_blob(blake3_hash: string): void;
|
|
326
|
+
/**
|
|
327
|
+
* get the secret key bytes for persistence (32 bytes)
|
|
328
|
+
* store this in IndexedDB to maintain the same identity across sessions
|
|
329
|
+
*/
|
|
330
|
+
secret_key(): Uint8Array;
|
|
331
|
+
/**
|
|
332
|
+
* start a background accept loop that handles incoming iroh-blobs connections.
|
|
333
|
+
*
|
|
334
|
+
* call this once after creating the node to allow remote peers to pull blobs
|
|
335
|
+
* from this node (e.g., for P2P music upload where the server pulls from browser).
|
|
336
|
+
*
|
|
337
|
+
* only handles iroh-blobs connections — other ALPNs are ignored (dropped).
|
|
338
|
+
* safe to call multiple times (subsequent calls are no-ops).
|
|
339
|
+
*
|
|
340
|
+
* WARNING: if you also call `accept()` from JS, both loops will compete for
|
|
341
|
+
* incoming connections and each will only see a subset. use one or the other,
|
|
342
|
+
* not both. freqhole uses `start_blob_server()`, skein uses `accept()`.
|
|
343
|
+
*
|
|
344
|
+
* NOTE: no application-level peer auth is applied here. iroh-blobs transfers
|
|
345
|
+
* are content-addressed (blake3 verified), so a peer can only download blobs
|
|
346
|
+
* they already know the hash of. peer filtering can be added later if needed.
|
|
347
|
+
*/
|
|
348
|
+
start_blob_server(): void;
|
|
349
|
+
/**
|
|
350
|
+
* connect to a freqhole radio broadcaster.
|
|
351
|
+
*
|
|
352
|
+
* callbacks (all called from JS land):
|
|
353
|
+
* - `on_hello(json: string)` — fires once when the server's Hello
|
|
354
|
+
* message arrives. payload is the JSON-encoded `HelloMessage`.
|
|
355
|
+
* - `on_meta(json: string)` — fires on each track change with the
|
|
356
|
+
* JSON-encoded `MetaMessage`.
|
|
357
|
+
* - `on_chunk(seq: number, is_init: boolean, bytes: Uint8Array)` —
|
|
358
|
+
* fires per audio chunk. `is_init = true` marks the start of a new
|
|
359
|
+
* track; the JS side should append it to the same SourceBuffer.
|
|
360
|
+
*
|
|
361
|
+
* returns a [`RadioHandle`] — keep a reference to it; dropping it stops
|
|
362
|
+
* playback and closes the iroh connection.
|
|
363
|
+
*/
|
|
364
|
+
tune_radio(peer_addr: string, station_id: string | null | undefined, on_hello: Function, on_meta: Function, on_chunk: Function): Promise<RadioHandle>;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* handle returned to JS for a tuned-in radio session. dropping the handle
|
|
369
|
+
* (or calling `leave()`) closes the iroh connection, which tears down both
|
|
370
|
+
* read loops.
|
|
371
|
+
*/
|
|
372
|
+
export class RadioHandle {
|
|
373
|
+
private constructor();
|
|
374
|
+
free(): void;
|
|
375
|
+
[Symbol.dispose](): void;
|
|
376
|
+
/**
|
|
377
|
+
* stop receiving audio + meta and close the connection.
|
|
378
|
+
*/
|
|
379
|
+
leave(): void;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* compute the blake3 hash of the given bytes and return as a hex string.
|
|
384
|
+
* this runs entirely in the browser — no network call needed.
|
|
385
|
+
*/
|
|
386
|
+
export function hash_blake3(data: Uint8Array): string;
|
|
387
|
+
|
|
388
|
+
export function start(): void;
|
package/midden.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* @ts-self-types="./midden.d.ts" */
|
|
2
|
+
|
|
3
|
+
import * as wasm from "./midden_bg.wasm";
|
|
4
|
+
import { __wbg_set_wasm } from "./midden_bg.js";
|
|
5
|
+
__wbg_set_wasm(wasm);
|
|
6
|
+
wasm.__wbindgen_start();
|
|
7
|
+
export {
|
|
8
|
+
BiStream, HelloImageResult, IntoUnderlyingByteSource, IntoUnderlyingSink, IntoUnderlyingSource, MiddenNode, RadioHandle, hash_blake3, start
|
|
9
|
+
} from "./midden_bg.js";
|