@freqhole/midden 0.1.31 → 0.2.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 +81 -58
- package/midden.d.ts +265 -21
- package/midden.js +2 -2
- package/midden_bg.js +920 -244
- package/midden_bg.wasm +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,82 +1,105 @@
|
|
|
1
1
|
# midden
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
midden
|
|
8
|
-
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
the unified wasm iroh node for the freqhole world of apps: a `MiddenNode` class that lets
|
|
4
|
+
browsers talk to freqhole peers over iroh p2p - api requests, verified blob streaming,
|
|
5
|
+
opfs-backed persistent blob storage, chunked import sessions, radio, and raw bidirectional
|
|
6
|
+
streams for app protocols. rust crate built with wasm-bindgen/wasm-pack; npm package
|
|
7
|
+
`@freqhole/midden`.
|
|
8
|
+
|
|
9
|
+
## architecture
|
|
10
|
+
|
|
11
|
+
```mermaid
|
|
12
|
+
graph LR
|
|
13
|
+
subgraph browser
|
|
14
|
+
APP[your app / worker]
|
|
15
|
+
MN[MiddenNode wasm]
|
|
16
|
+
OPFS[(opfs blob store)]
|
|
17
|
+
end
|
|
18
|
+
subgraph peers
|
|
19
|
+
P1[always-on peer<br/>grimoire / tumulus / any]
|
|
20
|
+
P2[another browser]
|
|
21
|
+
end
|
|
22
|
+
APP --> MN
|
|
23
|
+
MN --- OPFS
|
|
24
|
+
MN <-- "iroh p2p (relay discovery + direct)" --> P1 & P2
|
|
25
|
+
```
|
|
16
26
|
|
|
17
|
-
|
|
18
|
-
|
|
27
|
+
the default registered ALPN set is the app-neutral base (`freqhole/1`, automerge sync,
|
|
28
|
+
friendz, admin, events, iroh-blobs). app-specific ALPNs are passed via `extra_alpns` -
|
|
29
|
+
never hardcoded here.
|
|
19
30
|
|
|
20
|
-
|
|
21
|
-
make build
|
|
31
|
+
## structure
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
```
|
|
34
|
+
src/
|
|
35
|
+
lib.rs MiddenNode + options, bi-streams, blob transfer, import sessions
|
|
36
|
+
opfs_store/ persistent browser blob store (storage-generic core, native tests)
|
|
37
|
+
radio.rs radio streaming
|
|
38
|
+
pkg/ committed wasm-pack output - consumers file:-dep straight at it
|
|
39
|
+
Makefile build targets
|
|
25
40
|
```
|
|
26
41
|
|
|
27
|
-
|
|
42
|
+
`pkg/` is checked into git: rebuild it whenever `src/` changes or consumers won't see the
|
|
43
|
+
change.
|
|
28
44
|
|
|
29
|
-
##
|
|
45
|
+
## getting started
|
|
30
46
|
|
|
31
47
|
```typescript
|
|
32
|
-
import { MiddenNode } from "midden";
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
import { MiddenNode, MiddenNodeOptions } from "@freqhole/midden";
|
|
49
|
+
|
|
50
|
+
const options = new MiddenNodeOptions();
|
|
51
|
+
options.set_secret_key(mySecretKeyBytes); // omit to generate fresh
|
|
52
|
+
options.set_opfs_store_dir("myapp-blobs"); // persistent blob store
|
|
53
|
+
options.set_extra_alpns(["myapp/1"]); // app protocol ALPNs
|
|
54
|
+
const node = await MiddenNode.create_with_options(options);
|
|
55
|
+
console.log("node id:", node.node_id());
|
|
56
|
+
|
|
57
|
+
// request/response to a peer (json over iroh; same dispatch shape as http)
|
|
58
|
+
const resp = await node.api_request(peerAddr, "GET", "/api/hello", null);
|
|
59
|
+
|
|
60
|
+
// verified blob download with progress
|
|
61
|
+
const bytes = await node.download_verified_with_ensure_progress(
|
|
62
|
+
peerAddr,
|
|
63
|
+
blake3Hash,
|
|
64
|
+
totalSize,
|
|
65
|
+
(fraction) => {},
|
|
66
|
+
downloadId,
|
|
44
67
|
);
|
|
45
|
-
console.log(response.status, response.body);
|
|
46
68
|
|
|
47
|
-
//
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
69
|
+
// raw bidirectional stream for your own protocol
|
|
70
|
+
const stream = await node.open_bi(peerAddr, "myapp/1");
|
|
71
|
+
await stream.write_raw_and_finish(encodedRequest);
|
|
72
|
+
const reply = await stream.read_to_end(64 * 1024);
|
|
51
73
|
```
|
|
52
74
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
midden accepts two formats for `peer_addr`:
|
|
75
|
+
`peer_addr` accepts a bare 64-hex node id (relay discovery) or a full endpoint json
|
|
76
|
+
(`{"id":"...","addrs":[...]}` with relay/ip hints).
|
|
56
77
|
|
|
57
|
-
|
|
78
|
+
other surface: `import_blob`/`release_blob` (stage bytes for peers), `ImportSession`
|
|
79
|
+
(chunked streaming import with incremental blake3), `Blake3Hasher`, `CancelToken` +
|
|
80
|
+
`download_cancel` (pause/resume), `protect_blob`/`unprotect_blob` (gc pins),
|
|
81
|
+
`has_complete_blob`, opfs selftests, radio.
|
|
58
82
|
|
|
59
|
-
|
|
60
|
-
13a257b5367d6b5b7ceb67ec6246c3dafbe886af8ed429408cd7619c7a4787b1
|
|
61
|
-
```
|
|
83
|
+
## developer quick start
|
|
62
84
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"id": "13a257b5...",
|
|
67
|
-
"addrs": [{ "Relay": "https://..." }, { "Ip": "192.168.1.100:57383" }]
|
|
68
|
-
}
|
|
69
|
-
```
|
|
85
|
+
```bash
|
|
86
|
+
cargo install wasm-pack
|
|
87
|
+
brew install llvm # macos: wasm toolchain needs it
|
|
70
88
|
|
|
71
|
-
|
|
89
|
+
make build # dev build -> pkg/
|
|
90
|
+
make build-release # optimized
|
|
91
|
+
cargo test # native tests (opfs_store core runs without a browser)
|
|
92
|
+
```
|
|
72
93
|
|
|
73
|
-
|
|
94
|
+
consumers today: tomb/spume + rathole, skein/loam, playlistz - all via `file:` deps at
|
|
95
|
+
`pkg/` (e.g. `file:../../midden/pkg`). vite consumers bundling this inside a worker need
|
|
96
|
+
the resolveId-plugin pattern (see @freqhole/reliquary's README).
|
|
74
97
|
|
|
75
|
-
|
|
76
|
-
- messages: `ProxyRequest`, `ProxyResponse`, `BlobStreamRequest`, `BlobStreamResponse`
|
|
77
|
-
- blob streaming: length-prefixed header followed by raw bytes
|
|
98
|
+
## protocol
|
|
78
99
|
|
|
79
|
-
|
|
100
|
+
same wire protocol as grimoire's federation transport: ALPN `freqhole/1`, messages
|
|
101
|
+
`ApiRequest`/`ApiResponse`, `EnsureBlobRequest`/`EnsureBlobResponse`, blob streaming as a
|
|
102
|
+
length-prefixed header + raw bytes. see `grimoire/src/federation/transport/protocol.rs`.
|
|
80
103
|
|
|
81
104
|
---
|
|
82
105
|
|
package/midden.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* *This API requires the following crate features to be activated: `ReadableStreamType`*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
type ReadableStreamType = "bytes";
|
|
9
|
+
export type ReadableStreamType = "bytes";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* a bidirectional QUIC stream for length-delimited message exchange.
|
|
@@ -90,11 +90,57 @@ export class BiStream {
|
|
|
90
90
|
* race the QUIC flush -- the peer's `read_to_end` then errors with
|
|
91
91
|
* "connection lost" mid-payload because the in-flight frames are
|
|
92
92
|
* torn down with the connection. matters most for large payloads
|
|
93
|
-
* (e.g. base64-encoded blob bodies in `
|
|
93
|
+
* (e.g. base64-encoded blob bodies in `api_response`).
|
|
94
94
|
*/
|
|
95
95
|
write_raw_and_finish(data: Uint8Array): Promise<void>;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* incremental blake3 hasher for streaming uploads — feed fixed-size chunks
|
|
100
|
+
* via update() and read the final hex hash from finalize(). lets JS hash a
|
|
101
|
+
* File while streaming it (file.stream() reader loop) instead of holding
|
|
102
|
+
* the whole payload in memory for a one-shot hash_blake3().
|
|
103
|
+
*/
|
|
104
|
+
export class Blake3Hasher {
|
|
105
|
+
free(): void;
|
|
106
|
+
[Symbol.dispose](): void;
|
|
107
|
+
/**
|
|
108
|
+
* finish and return the hash as a 64-char hex string. the hasher can
|
|
109
|
+
* keep absorbing after this (blake3 finalize is non-destructive), but
|
|
110
|
+
* callers should treat the session as done.
|
|
111
|
+
*/
|
|
112
|
+
finalize(): string;
|
|
113
|
+
constructor();
|
|
114
|
+
/**
|
|
115
|
+
* absorb the next chunk of data.
|
|
116
|
+
*/
|
|
117
|
+
update(chunk: Uint8Array): void;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* cooperative cancellation for in-flight downloads (pause/cancel from JS).
|
|
122
|
+
* the download loops select on `cancelled()` between progress events —
|
|
123
|
+
* cancellation takes effect at the next event boundary, and the partial
|
|
124
|
+
* data stays in the store, so a later download of the same hash resumes
|
|
125
|
+
* from the persisted bitfield (only missing ranges transfer).
|
|
126
|
+
*/
|
|
127
|
+
export class CancelToken {
|
|
128
|
+
free(): void;
|
|
129
|
+
[Symbol.dispose](): void;
|
|
130
|
+
/**
|
|
131
|
+
* request cancellation. idempotent.
|
|
132
|
+
*/
|
|
133
|
+
cancel(): void;
|
|
134
|
+
/**
|
|
135
|
+
* return a new CancelToken sharing the same cancellation state.
|
|
136
|
+
* needed because passing a wasm class by value consumes the JS handle —
|
|
137
|
+
* callers keep the original and pass a clone into download calls.
|
|
138
|
+
*/
|
|
139
|
+
clone_token(): CancelToken;
|
|
140
|
+
is_cancelled(): boolean;
|
|
141
|
+
constructor();
|
|
142
|
+
}
|
|
143
|
+
|
|
98
144
|
/**
|
|
99
145
|
* result from fetching the server hello image from a peer
|
|
100
146
|
*/
|
|
@@ -106,6 +152,38 @@ export class HelloImageResult {
|
|
|
106
152
|
readonly data: Uint8Array;
|
|
107
153
|
}
|
|
108
154
|
|
|
155
|
+
/**
|
|
156
|
+
* chunked import session — the streaming counterpart to import_blob.
|
|
157
|
+
*
|
|
158
|
+
* created via MiddenNode::start_import(). JS feeds fixed-size chunks with
|
|
159
|
+
* push() (backpressured: the promise resolves only once the chunk is
|
|
160
|
+
* queued), then finish() completes the import and returns the blake3 hash.
|
|
161
|
+
* the wasm boundary never sees the whole payload at once; the store's
|
|
162
|
+
* ImportByteStream machinery computes the bao tree incrementally.
|
|
163
|
+
*
|
|
164
|
+
* the finished blob is pinned in the node's active_tags (same as
|
|
165
|
+
* import_blob) until release_blob() is called.
|
|
166
|
+
*/
|
|
167
|
+
export class ImportSession {
|
|
168
|
+
private constructor();
|
|
169
|
+
free(): void;
|
|
170
|
+
[Symbol.dispose](): void;
|
|
171
|
+
/**
|
|
172
|
+
* abort the import. any partially-imported data is left to GC.
|
|
173
|
+
*/
|
|
174
|
+
abort(): void;
|
|
175
|
+
/**
|
|
176
|
+
* signal end-of-stream, wait for the import to complete, pin the
|
|
177
|
+
* resulting blob, and return its blake3 hash as a hex string.
|
|
178
|
+
*/
|
|
179
|
+
finish(): Promise<string>;
|
|
180
|
+
/**
|
|
181
|
+
* queue the next chunk. resolves once the chunk has been accepted by
|
|
182
|
+
* the import stream (bounded channel — this is the backpressure point).
|
|
183
|
+
*/
|
|
184
|
+
push(chunk: Uint8Array): Promise<void>;
|
|
185
|
+
}
|
|
186
|
+
|
|
109
187
|
export class IntoUnderlyingByteSource {
|
|
110
188
|
private constructor();
|
|
111
189
|
free(): void;
|
|
@@ -138,7 +216,7 @@ export class IntoUnderlyingSource {
|
|
|
138
216
|
* browser P2P node for freqhole federation
|
|
139
217
|
*
|
|
140
218
|
* supports two protocols:
|
|
141
|
-
* - freqhole/1: API
|
|
219
|
+
* - freqhole/1: API requests and small blob streaming
|
|
142
220
|
* - iroh-blobs: verified streaming for audio files
|
|
143
221
|
*/
|
|
144
222
|
export class MiddenNode {
|
|
@@ -161,6 +239,16 @@ export class MiddenNode {
|
|
|
161
239
|
* return the number of blobs currently held in the store via active TempTags.
|
|
162
240
|
*/
|
|
163
241
|
active_blob_count(): number;
|
|
242
|
+
/**
|
|
243
|
+
* send an API request to a peer
|
|
244
|
+
* peer_addr can be plain node_id or full endpoint JSON with relay/IP hints
|
|
245
|
+
*/
|
|
246
|
+
api_request(peer_addr: string, method: string, path: string, body?: string | null): Promise<any>;
|
|
247
|
+
/**
|
|
248
|
+
* PROTOTYPE: remove a hash's restriction, returning it to the default
|
|
249
|
+
* (served to anyone) state.
|
|
250
|
+
*/
|
|
251
|
+
clear_blob_restriction(blake3_hash: string): void;
|
|
164
252
|
/**
|
|
165
253
|
* compute blake3 hash for a blob on demand
|
|
166
254
|
*
|
|
@@ -172,22 +260,43 @@ export class MiddenNode {
|
|
|
172
260
|
*/
|
|
173
261
|
compute_blake3(peer_addr: string, blob_id: string): Promise<string | undefined>;
|
|
174
262
|
/**
|
|
175
|
-
* create a new node with random identity
|
|
176
|
-
* waits for relay connection before returning
|
|
263
|
+
* create a new node with random identity, an in-memory blob store, and
|
|
264
|
+
* the default ALPN set. waits for relay connection before returning.
|
|
265
|
+
*
|
|
266
|
+
* deprecated: use `create_with_options` instead.
|
|
267
|
+
*
|
|
268
|
+
* `connect_timeout_ms` is an optional per-dial timeout for `open_bi`
|
|
269
|
+
* (defaults to 10s when omitted/undefined).
|
|
177
270
|
*/
|
|
178
|
-
static create(): Promise<MiddenNode>;
|
|
271
|
+
static create(connect_timeout_ms?: number | null): Promise<MiddenNode>;
|
|
179
272
|
/**
|
|
180
273
|
* create a node from existing secret key bytes (for persistence)
|
|
181
|
-
* key_bytes must be exactly 32 bytes
|
|
274
|
+
* key_bytes must be exactly 32 bytes.
|
|
275
|
+
*
|
|
276
|
+
* deprecated: use `create_with_options` instead.
|
|
277
|
+
*
|
|
278
|
+
* `connect_timeout_ms` is an optional per-dial timeout for `open_bi`
|
|
279
|
+
* (defaults to 10s when omitted/undefined).
|
|
182
280
|
*/
|
|
183
|
-
static create_from_key(key_bytes: Uint8Array): Promise<MiddenNode>;
|
|
281
|
+
static create_from_key(key_bytes: Uint8Array, connect_timeout_ms?: number | null): Promise<MiddenNode>;
|
|
184
282
|
/**
|
|
185
283
|
* create a node from existing secret key with additional ALPN protocols.
|
|
186
284
|
*
|
|
285
|
+
* deprecated: use `create_with_options` instead.
|
|
286
|
+
*
|
|
187
287
|
* `extra_alpns` is a JS array of strings (e.g. ["iroh/automerge-repo/1"]).
|
|
188
|
-
* the node always registers
|
|
288
|
+
* the node always registers the default ALPN set plus whatever extra ALPNs are given.
|
|
289
|
+
*
|
|
290
|
+
* `connect_timeout_ms` is an optional per-dial timeout for `open_bi`
|
|
291
|
+
* (defaults to 10s when omitted/undefined).
|
|
292
|
+
*/
|
|
293
|
+
static create_with_alpns(key_bytes: Uint8Array, extra_alpns: Array<any>, connect_timeout_ms?: number | null): Promise<MiddenNode>;
|
|
294
|
+
/**
|
|
295
|
+
* create a node from an options bag. this is the single canonical
|
|
296
|
+
* constructor — `create`/`create_from_key`/`create_with_alpns` below
|
|
297
|
+
* are deprecated wrappers kept for existing callers (spume, playlistz).
|
|
189
298
|
*/
|
|
190
|
-
static
|
|
299
|
+
static create_with_options(options: MiddenNodeOptions): Promise<MiddenNode>;
|
|
191
300
|
/**
|
|
192
301
|
* download a blob using iroh-blobs verified streaming
|
|
193
302
|
*
|
|
@@ -209,6 +318,13 @@ export class MiddenNode {
|
|
|
209
318
|
* returns (blob_data, blake3_hash) for caching the hash for future requests.
|
|
210
319
|
*/
|
|
211
320
|
download_verified_by_id(peer_addr: string, blob_id: string): Promise<Array<any>>;
|
|
321
|
+
/**
|
|
322
|
+
* full pipeline from blob_id with progress reporting.
|
|
323
|
+
*
|
|
324
|
+
* computes blake3 on demand, then uses verified download with progress.
|
|
325
|
+
* returns [data: Uint8Array, blake3: string].
|
|
326
|
+
*/
|
|
327
|
+
download_verified_by_id_progress(peer_addr: string, blob_id: string, total_size: number, on_progress: Function): Promise<Array<any>>;
|
|
212
328
|
/**
|
|
213
329
|
* download a verified blob and stream chunks to JS via callback
|
|
214
330
|
*
|
|
@@ -229,13 +345,14 @@ export class MiddenNode {
|
|
|
229
345
|
*
|
|
230
346
|
* returns total bytes streamed.
|
|
231
347
|
*/
|
|
232
|
-
download_verified_streaming(peer_addr: string, blake3_hash: string, total_size: number, on_chunk: Function, on_progress: Function): Promise<number>;
|
|
348
|
+
download_verified_streaming(peer_addr: string, blake3_hash: string, total_size: number, on_chunk: Function, on_progress: Function, cancel?: CancelToken | null): Promise<number>;
|
|
233
349
|
/**
|
|
234
350
|
* streaming download with auto ensure+retry. first attempts the streaming
|
|
235
351
|
* download; if the verified download fails (blob not in peer's store), calls
|
|
236
|
-
* ensure_blob to load it, then retries.
|
|
352
|
+
* ensure_blob to load it, then retries. a deliberate cancellation is NOT
|
|
353
|
+
* retried — it propagates immediately with the "download cancelled" message.
|
|
237
354
|
*/
|
|
238
|
-
download_verified_streaming_with_ensure(peer_addr: string, blake3_hash: string, total_size: number, on_chunk: Function, on_progress: Function): Promise<number>;
|
|
355
|
+
download_verified_streaming_with_ensure(peer_addr: string, blake3_hash: string, total_size: number, on_chunk: Function, on_progress: Function, cancel?: CancelToken | null): Promise<number>;
|
|
239
356
|
/**
|
|
240
357
|
* download a blob using iroh-blobs with automatic ensure + retry
|
|
241
358
|
*
|
|
@@ -243,6 +360,35 @@ export class MiddenNode {
|
|
|
243
360
|
* calls ensure_blob to load it, then retries.
|
|
244
361
|
*/
|
|
245
362
|
download_verified_with_ensure(peer_addr: string, blake3_hash: string): Promise<Uint8Array>;
|
|
363
|
+
/**
|
|
364
|
+
* download with ensure + retry and progress reporting.
|
|
365
|
+
*
|
|
366
|
+
* tries download first; if blob not in peer's FsStore, calls ensure_blob
|
|
367
|
+
* then retries. progress callback receives fraction (0.0 to 1.0).
|
|
368
|
+
* `cancel`: optional cooperative cancellation (pause) — a deliberate
|
|
369
|
+
* cancellation is NOT retried, it propagates immediately.
|
|
370
|
+
*
|
|
371
|
+
* NOTE: any failure on the first attempt triggers this same
|
|
372
|
+
* ensure-then-retry fallback, not just the "blob not in FsStore yet"
|
|
373
|
+
* case the fallback was designed for. for a large blob, the first
|
|
374
|
+
* attempt can stream a substantial fraction of the bytes (driving
|
|
375
|
+
* `on_progress` most/all of the way to 1.0) before failing late, so the
|
|
376
|
+
* caller-visible symptom is a full 0->100% progress cycle that silently
|
|
377
|
+
* restarts from 0 for a second full cycle. logging the first attempt's
|
|
378
|
+
* error and explicitly resetting progress to 0 here makes this restart
|
|
379
|
+
* visible/diagnosable instead of looking like a silent glitch.
|
|
380
|
+
*/
|
|
381
|
+
download_verified_with_ensure_progress(peer_addr: string, blake3_hash: string, total_size: number, on_progress: Function, cancel?: CancelToken | null): Promise<Uint8Array>;
|
|
382
|
+
/**
|
|
383
|
+
* download a blob with progress reporting via JS callback
|
|
384
|
+
*
|
|
385
|
+
* same as download_verified but calls on_progress(fraction) where
|
|
386
|
+
* fraction is bytes_received / total_size (0.0 to 1.0).
|
|
387
|
+
* total_size should come from the caller's known size field.
|
|
388
|
+
* `cancel`: optional cooperative cancellation (pause) — see
|
|
389
|
+
* download_verified_streaming for the semantics.
|
|
390
|
+
*/
|
|
391
|
+
download_verified_with_progress(peer_addr: string, blake3_hash: string, total_size: number, on_progress: Function, cancel?: CancelToken | null): Promise<Uint8Array>;
|
|
246
392
|
/**
|
|
247
393
|
* ensure a blob is loaded into the peer's FsStore by blake3 hash
|
|
248
394
|
*
|
|
@@ -259,11 +405,18 @@ export class MiddenNode {
|
|
|
259
405
|
*/
|
|
260
406
|
fetch_hello_image(peer_addr: string): Promise<HelloImageResult>;
|
|
261
407
|
/**
|
|
262
|
-
* check whether a blob with the given blake3 hash is currently held in the
|
|
408
|
+
* check whether a blob with the given blake3 hash is currently held in the store
|
|
263
409
|
* via an active TempTag. avoids expensive OPFS read + bao recomputation when the
|
|
264
410
|
* blob is already loaded.
|
|
265
411
|
*/
|
|
266
412
|
has_active_blob(blake3_hash: string): boolean;
|
|
413
|
+
/**
|
|
414
|
+
* check whether a COMPLETE blob with this hash exists in the blob store
|
|
415
|
+
* itself — with the persistent opfs store this is true across reloads,
|
|
416
|
+
* even when no TempTag pins it. lets serving paths skip re-imports
|
|
417
|
+
* entirely.
|
|
418
|
+
*/
|
|
419
|
+
has_complete_blob(blake3_hash: string): Promise<boolean>;
|
|
267
420
|
/**
|
|
268
421
|
* import a blob from its pre-computed bao-encoded bytes, skipping the
|
|
269
422
|
* expensive bao tree computation. `blake3_hash` is the 64-char hex hash,
|
|
@@ -279,7 +432,7 @@ export class MiddenNode {
|
|
|
279
432
|
* import raw bytes into the iroh-blobs store, returning the blake3 hash.
|
|
280
433
|
* this makes the blob available for verified download by peers.
|
|
281
434
|
* the blob stays in the store as long as its TempTag is held in active_tags.
|
|
282
|
-
* call release_blob() to allow GC
|
|
435
|
+
* call release_blob() to allow GC.
|
|
283
436
|
*/
|
|
284
437
|
import_blob(data: Uint8Array): Promise<string>;
|
|
285
438
|
/**
|
|
@@ -290,6 +443,16 @@ export class MiddenNode {
|
|
|
290
443
|
* returns a JS object: `{ hash: string, bao: Uint8Array }`
|
|
291
444
|
*/
|
|
292
445
|
import_blob_and_export_bao(data: Uint8Array): Promise<any>;
|
|
446
|
+
/**
|
|
447
|
+
* get our full endpoint address as JSON (node_id + relay url + any direct addrs).
|
|
448
|
+
*
|
|
449
|
+
* after `online()` has resolved this includes the home relay url, which is
|
|
450
|
+
* enough for a remote peer to dial us directly via the relay without doing
|
|
451
|
+
* a pkarr/DNS discovery lookup first. pass the returned string straight to
|
|
452
|
+
* `open_bi`/`connect` on the other side - `parse_peer_addr` accepts this
|
|
453
|
+
* same JSON shape. avoids the discovery propagation race on fresh boots.
|
|
454
|
+
*/
|
|
455
|
+
node_addr(): string;
|
|
293
456
|
/**
|
|
294
457
|
* get our node_id (iroh public key)
|
|
295
458
|
*/
|
|
@@ -298,12 +461,18 @@ export class MiddenNode {
|
|
|
298
461
|
* open a bidirectional stream to a peer on a specific ALPN.
|
|
299
462
|
*
|
|
300
463
|
* `peer_addr` can be a plain node_id hex string or a full endpoint
|
|
301
|
-
* address JSON (same format as
|
|
464
|
+
* address JSON (same format as api_request). `alpn` is the protocol
|
|
302
465
|
* to negotiate (e.g. "iroh/automerge-repo/1").
|
|
303
466
|
*
|
|
304
467
|
* returns a BiStream for length-delimited message exchange.
|
|
305
468
|
*/
|
|
306
469
|
open_bi(peer_addr: string, alpn: string): Promise<BiStream>;
|
|
470
|
+
/**
|
|
471
|
+
* pin a hash so gc won't sweep it (e.g. a paused partial download).
|
|
472
|
+
* idempotent. pair with unprotect_blob when the partial is resumed to
|
|
473
|
+
* completion or discarded.
|
|
474
|
+
*/
|
|
475
|
+
protect_blob(blake3_hash: string): void;
|
|
307
476
|
/**
|
|
308
477
|
* dispatch a typed admin command to a peer over the freqhole-admin/1 ALPN.
|
|
309
478
|
*
|
|
@@ -313,16 +482,23 @@ export class MiddenNode {
|
|
|
313
482
|
* schema happens in the spume `AdminClient`.
|
|
314
483
|
*/
|
|
315
484
|
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
485
|
/**
|
|
322
486
|
* release a blob's TempTag, allowing the store to garbage-collect it.
|
|
323
487
|
* blake3_hash should be the 64-char hex string returned by import_blob.
|
|
324
488
|
*/
|
|
325
489
|
release_blob(blake3_hash: string): void;
|
|
490
|
+
/**
|
|
491
|
+
* PROTOTYPE: restrict a blob (by blake3 hex hash) so only the given
|
|
492
|
+
* peer node ids may fetch it over the `iroh-blobs/*` ALPN. a hash with
|
|
493
|
+
* no restriction registered is served to anyone (today's default
|
|
494
|
+
* behavior, unchanged) — calling this is what opts a specific hash
|
|
495
|
+
* into gating.
|
|
496
|
+
*
|
|
497
|
+
* this is a stopgap/demo hook, not the real canvas-ACL integration: it
|
|
498
|
+
* has to be called explicitly, from JS, with an already-resolved list
|
|
499
|
+
* of allowed peer node ids for this one hash.
|
|
500
|
+
*/
|
|
501
|
+
restrict_blob_to_peers(blake3_hash: string, peer_node_ids: Array<any>): void;
|
|
326
502
|
/**
|
|
327
503
|
* get the secret key bytes for persistence (32 bytes)
|
|
328
504
|
* store this in IndexedDB to maintain the same identity across sessions
|
|
@@ -346,6 +522,12 @@ export class MiddenNode {
|
|
|
346
522
|
* they already know the hash of. peer filtering can be added later if needed.
|
|
347
523
|
*/
|
|
348
524
|
start_blob_server(): void;
|
|
525
|
+
/**
|
|
526
|
+
* begin a chunked import — the streaming counterpart to import_blob for
|
|
527
|
+
* payloads that shouldn't be materialized as one contiguous &[u8] across
|
|
528
|
+
* the wasm boundary. see ImportSession for the push/finish protocol.
|
|
529
|
+
*/
|
|
530
|
+
start_import(): ImportSession;
|
|
349
531
|
/**
|
|
350
532
|
* connect to a freqhole radio broadcaster.
|
|
351
533
|
*
|
|
@@ -362,6 +544,53 @@ export class MiddenNode {
|
|
|
362
544
|
* playback and closes the iroh connection.
|
|
363
545
|
*/
|
|
364
546
|
tune_radio(peer_addr: string, station_id: string | null | undefined, on_hello: Function, on_meta: Function, on_chunk: Function): Promise<RadioHandle>;
|
|
547
|
+
/**
|
|
548
|
+
* remove a gc pin added by protect_blob (or by a cancelled download).
|
|
549
|
+
*/
|
|
550
|
+
unprotect_blob(blake3_hash: string): void;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* options bag for `MiddenNode::create_with_options`, the single canonical
|
|
555
|
+
* constructor. build one, set whichever fields are needed, and pass it in:
|
|
556
|
+
*
|
|
557
|
+
* ```js
|
|
558
|
+
* const opts = new MiddenNodeOptions();
|
|
559
|
+
* opts.opfs_store_dir = "midden-blob-store";
|
|
560
|
+
* opts.connect_timeout_ms = 5000;
|
|
561
|
+
* const node = await MiddenNode.create_with_options(opts);
|
|
562
|
+
* ```
|
|
563
|
+
*
|
|
564
|
+
* `create`/`create_from_key`/`create_with_alpns` remain as deprecated
|
|
565
|
+
* wrappers over this constructor for existing callers (spume, playlistz).
|
|
566
|
+
*/
|
|
567
|
+
export class MiddenNodeOptions {
|
|
568
|
+
free(): void;
|
|
569
|
+
[Symbol.dispose](): void;
|
|
570
|
+
constructor();
|
|
571
|
+
/**
|
|
572
|
+
* per-dial timeout (ms) for `open_bi`/`connect` (defaults to 10s).
|
|
573
|
+
*/
|
|
574
|
+
get connect_timeout_ms(): number | undefined;
|
|
575
|
+
set connect_timeout_ms(value: number | null | undefined);
|
|
576
|
+
/**
|
|
577
|
+
* additional ALPN protocols to register beyond the default set.
|
|
578
|
+
*/
|
|
579
|
+
get extra_alpns(): string[] | undefined;
|
|
580
|
+
set extra_alpns(value: string[] | null | undefined);
|
|
581
|
+
/**
|
|
582
|
+
* when given, blobs persist in an OPFS-backed store under this
|
|
583
|
+
* directory (worker context required); otherwise (or when OPFS is
|
|
584
|
+
* unavailable) an in-memory store is used.
|
|
585
|
+
*/
|
|
586
|
+
get opfs_store_dir(): string | undefined;
|
|
587
|
+
set opfs_store_dir(value: string | null | undefined);
|
|
588
|
+
/**
|
|
589
|
+
* the node's secret key (32 raw bytes). omit (or pass null/undefined)
|
|
590
|
+
* to generate a random identity.
|
|
591
|
+
*/
|
|
592
|
+
get secret_key(): Uint8Array | undefined;
|
|
593
|
+
set secret_key(value: Uint8Array | null | undefined);
|
|
365
594
|
}
|
|
366
595
|
|
|
367
596
|
/**
|
|
@@ -385,4 +614,19 @@ export class RadioHandle {
|
|
|
385
614
|
*/
|
|
386
615
|
export function hash_blake3(data: Uint8Array): string;
|
|
387
616
|
|
|
617
|
+
/**
|
|
618
|
+
* opfs store selftest — runs the full import/export round trip against
|
|
619
|
+
* real OPFS through the real iroh-blobs api. worker context required
|
|
620
|
+
* (sync access handles). wasm-only debug helper, used for manual
|
|
621
|
+
* debugging from the blob worker, not from automated tests.
|
|
622
|
+
*/
|
|
623
|
+
export function opfs_store_selftest(): Promise<string>;
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* persistence selftest: blobs + tags survive a store shutdown/reopen over
|
|
627
|
+
* the same OPFS directory. worker context required. wasm-only debug
|
|
628
|
+
* helper, used for manual debugging from the blob worker.
|
|
629
|
+
*/
|
|
630
|
+
export function opfs_store_selftest_persistence(): Promise<string>;
|
|
631
|
+
|
|
388
632
|
export function start(): void;
|
package/midden.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* @ts-self-types="./midden.d.ts" */
|
|
2
|
-
|
|
3
2
|
import * as wasm from "./midden_bg.wasm";
|
|
4
3
|
import { __wbg_set_wasm } from "./midden_bg.js";
|
|
4
|
+
|
|
5
5
|
__wbg_set_wasm(wasm);
|
|
6
6
|
wasm.__wbindgen_start();
|
|
7
7
|
export {
|
|
8
|
-
BiStream, HelloImageResult, IntoUnderlyingByteSource, IntoUnderlyingSink, IntoUnderlyingSource, MiddenNode, RadioHandle, hash_blake3, start
|
|
8
|
+
BiStream, Blake3Hasher, CancelToken, HelloImageResult, ImportSession, IntoUnderlyingByteSource, IntoUnderlyingSink, IntoUnderlyingSource, MiddenNode, MiddenNodeOptions, RadioHandle, hash_blake3, opfs_store_selftest, opfs_store_selftest_persistence, start
|
|
9
9
|
} from "./midden_bg.js";
|