beamdb 0.10.0 → 0.17.0
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 +249 -32
- package/beam.d.ts +36 -5
- package/beam.js +125 -24
- package/beam_bg.wasm +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- [Wire Protocol](#wire-protocol)
|
|
24
24
|
- [Configuration](#configuration)
|
|
25
25
|
- [Testing](#testing)
|
|
26
|
+
- [Benchmarks](#benchmarks)
|
|
26
27
|
- [Features](#features)
|
|
27
28
|
- [Security](#security)
|
|
28
29
|
- [Contributing](#contributing)
|
|
@@ -45,7 +46,7 @@ BEAM is a maintained fork of [rod](https://github.com/mmalmi/rod) — a from-scr
|
|
|
45
46
|
- **Real-time** — `on()` subscriptions deliver updates as they propagate through the mesh
|
|
46
47
|
- **Eventually consistent** — last-write-wins conflict resolution via timestamps (matching Gun.js)
|
|
47
48
|
- **Encrypted** — SEA layer provides Ed25519 signing, X25519 ECDH, and AES-256-GCM encryption
|
|
48
|
-
- **Persistent** — `redb` embedded database for
|
|
49
|
+
- **Persistent** — `redb` embedded database (default), `fjall` LSM-tree (recommended for multi-node), `Persy` for high-concurrency, or in-memory for ephemeral use
|
|
49
50
|
- **Multi-transport** — WebSocket (relay), UDP multicast (LAN discovery), WebRTC (direct P2P)
|
|
50
51
|
- **Browser-ready** — compiles to WebAssembly via `wasm-pack`; same engine, same wire protocol, IndexedDB persistence
|
|
51
52
|
|
|
@@ -55,7 +56,7 @@ BEAM is a maintained fork of [rod](https://github.com/mmalmi/rod) — a from-scr
|
|
|
55
56
|
|
|
56
57
|
```toml
|
|
57
58
|
[dependencies]
|
|
58
|
-
beamdb = "0.
|
|
59
|
+
beamdb = "0.16"
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
Or via the CLI:
|
|
@@ -64,14 +65,17 @@ Or via the CLI:
|
|
|
64
65
|
cargo add beamdb
|
|
65
66
|
```
|
|
66
67
|
|
|
67
|
-
Feature flags (
|
|
68
|
+
Feature flags (all off by default):
|
|
68
69
|
|
|
69
70
|
```toml
|
|
70
71
|
# WebRTC direct P2P support
|
|
71
|
-
beamdb = { version = "0.
|
|
72
|
+
beamdb = { version = "0.16", features = ["webrtc"] }
|
|
73
|
+
|
|
74
|
+
# Fjall LSM-tree storage backend (recommended for multi-node deployments)
|
|
75
|
+
beamdb = { version = "0.16", features = ["fjall"] }
|
|
72
76
|
|
|
73
77
|
# Persy storage backend (for high-concurrency workloads)
|
|
74
|
-
beamdb = { version = "0.
|
|
78
|
+
beamdb = { version = "0.16", features = ["persy"] }
|
|
75
79
|
```
|
|
76
80
|
|
|
77
81
|
---
|
|
@@ -111,7 +115,8 @@ await init();
|
|
|
111
115
|
|
|
112
116
|
// Create a BEAM node
|
|
113
117
|
const beam = new Beam(); // in-memory (lost on reload)
|
|
114
|
-
// or: const beam = Beam.new_persistent();
|
|
118
|
+
// or: const beam = Beam.new_persistent(); // IndexedDB (survives reload)
|
|
119
|
+
// or: const beam = Beam.new_with_opfs(); // OPFS (survives reload, faster)
|
|
115
120
|
|
|
116
121
|
// Connect to a relay server
|
|
117
122
|
beam.connect("wss://relay.example.com/ws");
|
|
@@ -222,17 +227,26 @@ compatible WebSocket peer.
|
|
|
222
227
|
| Backend | Persistent | Browser API | Use Case |
|
|
223
228
|
|---------|-----------|-------------|----------|
|
|
224
229
|
| `MemoryStorage` | No (lost on reload) | Default | Ephemeral data, testing |
|
|
225
|
-
| `WasmIdbStorage` | Yes (IndexedDB) | Opt-in | Production browser apps |
|
|
230
|
+
| `WasmIdbStorage` | Yes (IndexedDB) | Opt-in via `new_persistent()` | Production browser apps |
|
|
231
|
+
| `WasmOpfsStorage` | Yes (OPFS) | Opt-in via `new_with_opfs()` | Modern browsers (Chrome 102+, Firefox 111+, Safari 15.2+) |
|
|
232
|
+
| `WasmNodeFsStorage` | Yes (Node.js fs) | Node.js only (`--features node-fs`) | Server-side WASM, Electron |
|
|
226
233
|
|
|
227
|
-
|
|
234
|
+
**WasmIdbStorage** uses a write-through cache: writes go to an in-memory `HashMap`
|
|
228
235
|
(fast reads) and are simultaneously written through to IndexedDB (persistence).
|
|
236
|
+
Data is serialized as postcard bytes (base64-encoded for IDB string storage),
|
|
237
|
+
with automatic JSON fallback for backward compatibility with pre-v0.17 databases.
|
|
229
238
|
On page reload, data is read back from IndexedDB into the cache.
|
|
230
239
|
|
|
240
|
+
**WasmOpfsStorage** uses the Origin Private File System for file-based persistence.
|
|
241
|
+
Data is stored as postcard-serialized binary files in OPFS, offering better
|
|
242
|
+
performance than IndexedDB for larger datasets. Requires a secure context
|
|
243
|
+
(HTTPS or localhost).
|
|
244
|
+
|
|
231
245
|
### Browser Constraints
|
|
232
246
|
|
|
233
247
|
- **Single-threaded** — all async work runs on the browser's main thread
|
|
234
248
|
- **Client-only** — connects to relays, does not accept inbound connections
|
|
235
|
-
- **No file system** — uses IndexedDB instead of redb/Persy
|
|
249
|
+
- **No native file system** — uses IndexedDB or OPFS instead of redb/Persy
|
|
236
250
|
- **WebSocket only** — no UDP multicast or WebRTC (browser sandbox limitations)
|
|
237
251
|
|
|
238
252
|
### Interop with Gun.js
|
|
@@ -381,7 +395,8 @@ BEAM is built on an actor model with a central router. Every component — stora
|
|
|
381
395
|
│ │ │ │ │ │
|
|
382
396
|
│ MemoryStorage│ │ WsServer │ │ WebRtcPeer │
|
|
383
397
|
│ RedbStorage │ │ WsClient │ │ (str0m) │
|
|
384
|
-
│
|
|
398
|
+
│ FjallStorage│ │ Multicast │ │ │
|
|
399
|
+
│ PersyStorage│ │ │ │ │
|
|
385
400
|
└─────────────┘ └─────────────┘ └────────────┘
|
|
386
401
|
```
|
|
387
402
|
|
|
@@ -411,6 +426,7 @@ BEAM is built on an actor model with a central router. Every component — stora
|
|
|
411
426
|
| `sea/session/` | Session persistence: `MemorySessionStorage` (ephemeral) and `EncryptedFileSessionStorage` (disk, AES-GCM) |
|
|
412
427
|
| `adapters/memory_storage.rs` | In-memory `HashMap` storage (ephemeral, default for `Node::new()`) |
|
|
413
428
|
| `adapters/redb_storage.rs` | Persistent storage via `redb` embedded database — `BatchPut` atomic transactions, flush ack |
|
|
429
|
+
| `adapters/fjall_storage.rs` | Persistent storage via `fjall` LSM-tree database — WAL journalling, LZ4 compression, recommended for multi-node (feature-gated) |
|
|
414
430
|
| `adapters/persy_storage.rs` | Persistent storage via `Persy` segment store — high-concurrency writes, optional `background_ops` |
|
|
415
431
|
| `adapters/ws_server.rs` | WebSocket server: accepts inbound connections, spawns `WsConn` per connection, optional TLS, web UI on port+1 |
|
|
416
432
|
| `adapters/ws_client.rs` | `OutgoingWebsocketManager` — connects to remote WebSocket peers with retry |
|
|
@@ -628,11 +644,11 @@ When `allow_public_space=false`, the node rejects unsigned puts to public space
|
|
|
628
644
|
|
|
629
645
|
## Storage Backends
|
|
630
646
|
|
|
631
|
-
BEAM supports
|
|
647
|
+
BEAM supports three persistent storage backends for the embedded database layer. All implement the same `Actor` trait, so the rest of the codebase is unaware of which one is active. The wire protocol is backend-agnostic — nodes with different storage choices converge via the standard mesh.
|
|
632
648
|
|
|
633
649
|
### redb (Default)
|
|
634
650
|
|
|
635
|
-
**What**: Embedded ACID database, single-writer, fsync on every Put.
|
|
651
|
+
**What**: Embedded ACID B+tree database, single-writer, fsync on every Put.
|
|
636
652
|
|
|
637
653
|
**When to use**:
|
|
638
654
|
- Single-node deployments
|
|
@@ -642,16 +658,45 @@ BEAM supports two persistent storage backends for the embedded database layer. B
|
|
|
642
658
|
|
|
643
659
|
**Trade-offs**:
|
|
644
660
|
- ✅ Battle-tested, single-crate, well-understood
|
|
645
|
-
- ✅ fsync before ack = bulletproof durability
|
|
661
|
+
- ✅ fsync before ack = bulletproof durability — data survives power loss
|
|
662
|
+
- ✅ Best read performance (mmap'd B+tree = direct memory access)
|
|
646
663
|
- ❌ Single-writer serialization limits concurrent write throughput
|
|
647
664
|
- ❌ Not ideal for high-fanout mesh workloads
|
|
665
|
+
- ❌ Every Put = fsync (milliseconds, blocking)
|
|
666
|
+
|
|
667
|
+
### fjall (Recommended for Multi-Node)
|
|
668
|
+
|
|
669
|
+
**What**: Embedded LSM-tree (RocksDB-like) database in 100% safe Rust. WAL journalling with background compaction and built-in LZ4 compression.
|
|
670
|
+
|
|
671
|
+
**When to use**:
|
|
672
|
+
- Multi-node P2P deployments with high write fanout
|
|
673
|
+
- Workloads where peers flood puts during resync
|
|
674
|
+
- You want maximum write throughput
|
|
675
|
+
|
|
676
|
+
**Trade-offs**:
|
|
677
|
+
- ✅ 3–4× faster writes than redb (journal append vs fsync per write)
|
|
678
|
+
- ✅ Built-in LZ4 compression (free, SSTable-level)
|
|
679
|
+
- ✅ WriteBatch — single journal entry for atomic multi-put
|
|
680
|
+
- ✅ 100% safe Rust, no unsafe blocks
|
|
681
|
+
- ❌ ~1.4× slower random reads than redb (multi-level LSM lookup vs B+tree)
|
|
682
|
+
- ❌ Not fsync'd per write — data is crash-safe (WAL) but a power loss may lose recent un-fsync'd writes
|
|
683
|
+
- ❌ Background compaction causes read latency variance
|
|
684
|
+
|
|
685
|
+
**Durability model**: fjall's default matches RocksDB — writes are crash-safe via WAL (survive process crash), but not fsync'd to disk until explicit `persist()`. For a P2P database where peers hold copies of the data, this is the correct trade-off: if one node loses its WAL on power failure, peers resync it. `Flush` triggers `persist(SyncAll)` for full durability.
|
|
686
|
+
|
|
687
|
+
**Benchmarks** (see [`bench/RESULTS.md`](bench/RESULTS.md)):
|
|
688
|
+
|
|
689
|
+
| Benchmark | redb | fjall |
|
|
690
|
+
|---|---|---|
|
|
691
|
+
| write_storm (sequential) | ~977 elem/s | ~3,000 elem/s |
|
|
692
|
+
| concurrent_write_storm (4 tasks) | ~1,195 elem/s | ~4,836 elem/s |
|
|
693
|
+
| read_storm (random) | ~610 elem/s | ~447 elem/s |
|
|
648
694
|
|
|
649
695
|
### Persy (Opt-In)
|
|
650
696
|
|
|
651
697
|
**What**: Embedded segment-based store with per-transaction isolation and optional `background_ops` fsync offloading.
|
|
652
698
|
|
|
653
699
|
**When to use**:
|
|
654
|
-
- Multi-node meshes with high concurrent write fanout
|
|
655
700
|
- Workloads where many writers hit disjoint keys simultaneously
|
|
656
701
|
- You're benchmarking and Persy shows wins on your data
|
|
657
702
|
|
|
@@ -659,41 +704,82 @@ BEAM supports two persistent storage backends for the embedded database layer. B
|
|
|
659
704
|
- ✅ Multiple writers proceed in parallel on disjoint keys
|
|
660
705
|
- ✅ Optional `background_ops` for fsync offloading
|
|
661
706
|
- ❌ Younger ecosystem, fewer Stack Overflow answers
|
|
662
|
-
- ❌
|
|
707
|
+
- ❌ Author has acknowledged crash-safety issues; development has slowed
|
|
708
|
+
- ❌ No WASM path (native-only)
|
|
663
709
|
- ❌ Performance characteristics need your own benchmarks
|
|
664
710
|
|
|
711
|
+
### Comparison Summary
|
|
712
|
+
|
|
713
|
+
| | redb | fjall | Persy |
|
|
714
|
+
|---|---|---|---|
|
|
715
|
+
| **Structure** | B+tree | LSM-tree | Segment store |
|
|
716
|
+
| **Write path** | fsync per Put | WAL journal append | Per-tx isolation |
|
|
717
|
+
| **Durability** | Bulletproof (fsync) | Crash-safe (WAL), not power-safe | Per-tx |
|
|
718
|
+
| **Read speed** | Fastest (mmap) | Slower (multi-level) | Moderate |
|
|
719
|
+
| **Write speed** | Slowest (fsync) | Fastest (journal) | Moderate |
|
|
720
|
+
| **Concurrency** | Single-writer | Multi-writer | Multi-writer |
|
|
721
|
+
| **Compression** | None | LZ4 (free) | None |
|
|
722
|
+
| **WASM** | No | No | No |
|
|
723
|
+
| **Maturity** | Most mature | Active dev | Slowing dev |
|
|
724
|
+
| **Best for** | Single-node | Multi-node P2P | High-concurrency |
|
|
725
|
+
|
|
665
726
|
### Selection
|
|
666
727
|
|
|
667
|
-
|
|
728
|
+
Storage backends are **build-time** features, not runtime flags:
|
|
668
729
|
|
|
669
730
|
```bash
|
|
670
731
|
# Default build — redb only
|
|
671
732
|
cargo build --release --bin beam
|
|
672
733
|
|
|
673
|
-
# With
|
|
734
|
+
# With fjall support
|
|
735
|
+
cargo build --release --bin beam --features fjall
|
|
736
|
+
|
|
737
|
+
# With Persy and/or fjall support (enables migration subcommand)
|
|
674
738
|
cargo build --release --bin beam --features persy
|
|
739
|
+
cargo build --release --bin beam --features fjall
|
|
675
740
|
|
|
676
741
|
# Run with redb (default)
|
|
677
|
-
cargo run --release --bin beam -- --port 4944
|
|
742
|
+
cargo run --release --bin beam -- start --port 4944
|
|
678
743
|
|
|
679
744
|
# In-memory only (no persistence)
|
|
680
|
-
cargo run --release --bin beam -- --port 4944 --memory-storage true
|
|
745
|
+
cargo run --release --bin beam -- start --port 4944 --memory-storage true
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
**Library usage** — use any backend programmatically (requires corresponding feature flags):
|
|
749
|
+
|
|
750
|
+
```rust,ignore
|
|
751
|
+
use beam::adapters::{FjallStorage, RedbStorage, MemoryStorage};
|
|
752
|
+
|
|
753
|
+
// fjall (recommended for multi-node, requires --features fjall)
|
|
754
|
+
let storage = FjallStorage::new_with_config(Config::default(), "beam.fjall");
|
|
755
|
+
|
|
756
|
+
// redb (default, best for single-node)
|
|
757
|
+
let storage = RedbStorage::new_with_config(Config::default(), "beam.redb", None);
|
|
758
|
+
|
|
759
|
+
// in-memory (ephemeral)
|
|
760
|
+
let storage = MemoryStorage::new();
|
|
681
761
|
```
|
|
682
762
|
|
|
683
763
|
### Migration Between Backends
|
|
684
764
|
|
|
685
|
-
The `beam migrate` subcommand converts between formats (requires `--features persy`
|
|
765
|
+
The `beam migrate` subcommand converts between all supported storage formats (requires `--features persy` and/or `--features fjall`):
|
|
686
766
|
|
|
687
767
|
```bash
|
|
688
768
|
# Preview without writing
|
|
689
769
|
beam migrate --from redb --to persy --source ./data.redb --target ./data.persy --dry-run
|
|
690
770
|
|
|
691
|
-
#
|
|
771
|
+
# redb ↔ persy
|
|
692
772
|
beam migrate --from redb --to persy --source ./data.redb --target ./data.persy
|
|
693
|
-
|
|
694
|
-
# Reverse direction
|
|
695
773
|
beam migrate --from persy --to redb --source ./data.persy --target ./data.redb
|
|
696
774
|
|
|
775
|
+
# redb ↔ fjall (fjall uses a directory path, not a file)
|
|
776
|
+
beam migrate --from redb --to fjall --source ./data.redb --target ./data.fjall
|
|
777
|
+
beam migrate --from fjall --to redb --source ./data.fjall --target ./data.redb
|
|
778
|
+
|
|
779
|
+
# fjall ↔ persy
|
|
780
|
+
beam migrate --from fjall --to persy --source ./data.fjall --target ./data.persy
|
|
781
|
+
beam migrate --from persy --to fjall --source ./data.persy --target ./data.fjall
|
|
782
|
+
|
|
697
783
|
# Overwrite existing target
|
|
698
784
|
beam migrate --from redb --to persy --source ./data.redb --target ./data.persy --force
|
|
699
785
|
|
|
@@ -701,11 +787,11 @@ beam migrate --from redb --to persy --source ./data.redb --target ./data.persy -
|
|
|
701
787
|
beam migrate --from redb --to persy --source ./data.redb --target ./data.persy --batch-size 5000
|
|
702
788
|
```
|
|
703
789
|
|
|
704
|
-
Migration uses
|
|
790
|
+
Migration uses a reader/writer architecture with a canonical intermediate format — each backend has one reader and one writer. Adding a new storage backend requires only two functions, not N² pairwise paths. See `docs/migrations/migration-guide.md` for the full procedure including rollback.
|
|
705
791
|
|
|
706
792
|
### Mixed Meshes
|
|
707
793
|
|
|
708
|
-
Nodes with different storage backends interoperate transparently. A redb node, a Persy node, and an in-memory node form a valid mesh. The wire protocol carries the data; storage is a local choice.
|
|
794
|
+
Nodes with different storage backends interoperate transparently. A redb node, a fjall node, a Persy node, and an in-memory node form a valid mesh. The wire protocol carries the data; storage is a local choice.
|
|
709
795
|
|
|
710
796
|
**Cross-backend mesh verified** by `tests/cross_backend_mesh_e2e.rs`: 2 redb nodes + 1 Persy node converge correctly under the standard Put/Get protocol.
|
|
711
797
|
|
|
@@ -713,6 +799,7 @@ Nodes with different storage backends interoperate transparently. A redb node, a
|
|
|
713
799
|
|
|
714
800
|
- The `beam_meta_v1` metadata table from redb (last-write timestamps) is not preserved when migrating redb → Persy. This metadata is not currently used by the actor framework, so the loss is cosmetic.
|
|
715
801
|
- The migration tool is single-threaded per batch. For datasets larger than ~100k records, run during a maintenance window.
|
|
802
|
+
- fjall uses a directory path for storage (LSM-tree), while redb and Persy use single files. Migration involving fjall creates a directory at the target path.
|
|
716
803
|
|
|
717
804
|
---
|
|
718
805
|
|
|
@@ -785,10 +872,10 @@ BEAM uses Gun.js's JSON wire format. Messages are JSON objects with these fields
|
|
|
785
872
|
|
|
786
873
|
| Flag | Required | Description |
|
|
787
874
|
|------|----------|-------------|
|
|
788
|
-
| `--from` | Yes | Source backend: `redb` or `
|
|
789
|
-
| `--to` | Yes | Target backend: `redb` or `
|
|
790
|
-
| `--source` | Yes | Path to source database file |
|
|
791
|
-
| `--target` | Yes | Path to target database file
|
|
875
|
+
| `--from` | Yes | Source backend: `redb`, `persy`, or `fjall` |
|
|
876
|
+
| `--to` | Yes | Target backend: `redb`, `persy`, or `fjall` |
|
|
877
|
+
| `--source` | Yes | Path to source database (file for redb/persy, directory for fjall) |
|
|
878
|
+
| `--target` | Yes | Path to target database (file for redb/persy, directory for fjall) |
|
|
792
879
|
| `--batch-size` | No | Records per batch (default: 1000) |
|
|
793
880
|
| `--force` | No | Overwrite target if it already exists |
|
|
794
881
|
| `--dry-run` | No | Preview without writing |
|
|
@@ -803,6 +890,9 @@ let config = Config {
|
|
|
803
890
|
my_pub: Some("x.y".into()),
|
|
804
891
|
broadcast_buffer_size: 4096,
|
|
805
892
|
ice_servers: vec!["stun:stun.l.google.com:19302".into()],
|
|
893
|
+
dedup_capacity: 100_000,
|
|
894
|
+
mailbox_capacity: 65536,
|
|
895
|
+
child_mailbox_capacity: 256,
|
|
806
896
|
};
|
|
807
897
|
# }
|
|
808
898
|
```
|
|
@@ -870,13 +960,24 @@ cargo test
|
|
|
870
960
|
# With WebRTC tests
|
|
871
961
|
cargo test --features webrtc
|
|
872
962
|
|
|
963
|
+
# With fjall storage tests
|
|
964
|
+
cargo test --features fjall
|
|
965
|
+
|
|
966
|
+
# With Persy tests (includes redb↔persy migration tests)
|
|
967
|
+
cargo test --features persy
|
|
968
|
+
|
|
969
|
+
# With fjall tests (includes redb↔fjall migration tests)
|
|
970
|
+
cargo test --features fjall
|
|
971
|
+
|
|
972
|
+
# With both (includes all migration path tests)
|
|
973
|
+
cargo test --features fjall,persy
|
|
974
|
+
|
|
873
975
|
# Lint (zero warnings required)
|
|
874
976
|
cargo clippy -- -D warnings
|
|
875
977
|
|
|
876
978
|
# Doctests only (verifies README code examples compile)
|
|
877
979
|
cargo test --doc
|
|
878
980
|
|
|
879
|
-
# Benchmarks
|
|
880
981
|
cargo bench
|
|
881
982
|
|
|
882
983
|
# Run a specific integration test
|
|
@@ -902,21 +1003,137 @@ cargo test --test wire_live -- --ignored # Layer 3: live integration (needs Nod
|
|
|
902
1003
|
| `redb_storage_persists` | Data survives restart with redb storage |
|
|
903
1004
|
| `redb_storage_flush_returns_ok` | Flush ack protocol |
|
|
904
1005
|
| `cross_backend_mesh_e2e` | 2 redb + 1 Persy nodes converge correctly |
|
|
1006
|
+
| `fjall_e2e` | 6 fjall storage tests: put-get, sequential, nested, LWW, flush, isolation (`--features fjall`) |
|
|
905
1007
|
| `wire_tests` | 36 golden JSON fixtures — wire protocol spec as tests |
|
|
906
1008
|
| `wire_live` | Live BEAM ↔ Gun.js bidirectional sync (4 scenarios) |
|
|
907
1009
|
|
|
908
1010
|
---
|
|
909
1011
|
|
|
1012
|
+
## Benchmarks
|
|
1013
|
+
|
|
1014
|
+
BEAM includes a comprehensive benchmarking suite covering relay throughput,
|
|
1015
|
+
micro-benchmarks for hot-path components, and storage performance.
|
|
1016
|
+
|
|
1017
|
+
### Local Put Throughput
|
|
1018
|
+
|
|
1019
|
+
Local (non-relay) puts through the actor pipeline — measures the full
|
|
1020
|
+
`Node::handle → Router::route → MemoryStorage::apply` path with no network I/O:
|
|
1021
|
+
|
|
1022
|
+
| Scenario | Messages | Throughput |
|
|
1023
|
+
|----------|----------|------------|
|
|
1024
|
+
| 1 sender × 10k | 10,000 | ~24,000–53,000 puts/sec |
|
|
1025
|
+
|
|
1026
|
+
Throughput varies with system load. On a dedicated machine with no
|
|
1027
|
+
competing processes, expect 50,000+ puts/sec. The bottleneck is
|
|
1028
|
+
`Value` cloning for broadcast channels — further gains require
|
|
1029
|
+
`Arc<Value>` to make cloning a refcount bump.
|
|
1030
|
+
|
|
1031
|
+
### Relay Throughput
|
|
1032
|
+
|
|
1033
|
+
Real WebSocket connections through a memory-only relay (no disk I/O):
|
|
1034
|
+
|
|
1035
|
+
| Scenario | Messages | Throughput |
|
|
1036
|
+
|----------|----------|------------|
|
|
1037
|
+
| 1 sender × 10k | 10,000 | ~5,300 msgs/sec |
|
|
1038
|
+
| 1 sender × 50k | 50,000 | ~10,600 msgs/sec |
|
|
1039
|
+
| 10 senders × 5k | 50,000 | ~11,400 msgs/sec |
|
|
1040
|
+
|
|
1041
|
+
The relay's internal processing (parse + dedup + route + serialize) runs in
|
|
1042
|
+
microseconds — the bottleneck is client-side `put().await`, not the relay.
|
|
1043
|
+
|
|
1044
|
+
### Micro-Benchmarks (Criterion)
|
|
1045
|
+
|
|
1046
|
+
| Operation | Time |
|
|
1047
|
+
|-----------|------|
|
|
1048
|
+
| Parse small Put JSON | 1,067 ns |
|
|
1049
|
+
| Parse medium Put JSON | 2.00 µs |
|
|
1050
|
+
| Serialize small Put JSON | 69 ns |
|
|
1051
|
+
| Parse Get | 677 ns |
|
|
1052
|
+
| Dedup check (fresh) | 274 µs |
|
|
1053
|
+
| Dedup check (duplicate) | 41.8 µs |
|
|
1054
|
+
| Actor mailbox send+recv | 309 µs |
|
|
1055
|
+
|
|
1056
|
+
### WASM Benchmarks (Node.js)
|
|
1057
|
+
|
|
1058
|
+
| Operation | WASM | Native | Ratio |
|
|
1059
|
+
|-----------|------|--------|-------|
|
|
1060
|
+
| Parse small Put | 7.9 µs | 1,067 ns | ~7.4× |
|
|
1061
|
+
| Serialize small Put | 8.6 µs | 69 ns | ~125× |
|
|
1062
|
+
| Parse Get | 4.5 µs | 677 ns | ~6.7× |
|
|
1063
|
+
|
|
1064
|
+
Run with: `wasm-pack test --node --no-default-features -- --nocapture`
|
|
1065
|
+
|
|
1066
|
+
### WASM Relay Throughput (Browser-only)
|
|
1067
|
+
|
|
1068
|
+
`web_sys::WebSocket` callbacks don't fire in Node.js `wasm-bindgen-test-runner`
|
|
1069
|
+
([known limitation](https://github.com/wasm-bindgen/wasm-bindgen/issues/4921)).
|
|
1070
|
+
Use the browser benchmark page to measure WASM relay throughput:
|
|
1071
|
+
|
|
1072
|
+
```bash
|
|
1073
|
+
cargo run -- start --port 4944 --memory-storage true --redb-storage false
|
|
1074
|
+
python3 -m http.server 8080 -d examples/
|
|
1075
|
+
# Open http://localhost:8080/bench.html in a browser
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
Previous v0.11.0 browser results: ~115–651 msgs/sec depending on batch size.
|
|
1079
|
+
|
|
1080
|
+
### Browser Benchmark
|
|
1081
|
+
|
|
1082
|
+
An interactive benchmark page is available at `examples/bench.html`:
|
|
1083
|
+
|
|
1084
|
+
```bash
|
|
1085
|
+
# Start a relay
|
|
1086
|
+
cargo run -- start --port 4944 --memory-storage true --redb-storage false
|
|
1087
|
+
|
|
1088
|
+
# Serve the benchmark page
|
|
1089
|
+
python3 -m http.server 8080 -d examples/
|
|
1090
|
+
|
|
1091
|
+
# Open in browser
|
|
1092
|
+
open http://localhost:8080/bench.html
|
|
1093
|
+
```
|
|
1094
|
+
|
|
1095
|
+
The browser benchmark measures:
|
|
1096
|
+
- **Relay TPS**: end-to-end throughput through a real relay
|
|
1097
|
+
- **Put throughput**: local WASM API fire-and-forget puts
|
|
1098
|
+
- **Get throughput**: local WASM API promise resolution
|
|
1099
|
+
- **Put→Get round-trip**: full local cycle
|
|
1100
|
+
|
|
1101
|
+
### Running Benchmarks
|
|
1102
|
+
|
|
1103
|
+
```bash
|
|
1104
|
+
# Relay throughput (release mode required)
|
|
1105
|
+
cargo test --release --test relay_throughput_bench -- --ignored --nocapture
|
|
1106
|
+
|
|
1107
|
+
# Micro-benchmarks (hot-path components)
|
|
1108
|
+
cargo bench --bench my_benchmark -- "wire_|dup_check|actor_mailbox"
|
|
1109
|
+
|
|
1110
|
+
# Storage benchmarks (redb only by default)
|
|
1111
|
+
cargo bench --bench my_benchmark -- "write_storm|read_storm|mixed"
|
|
1112
|
+
|
|
1113
|
+
# Storage benchmarks with fjall (head-to-head comparison)
|
|
1114
|
+
cargo bench --features fjall --bench my_benchmark -- "write_storm|read_storm|mixed"
|
|
1115
|
+
|
|
1116
|
+
# Storage benchmarks with persy
|
|
1117
|
+
cargo bench --features persy --bench my_benchmark -- "write_storm|read_storm|mixed"
|
|
1118
|
+
|
|
1119
|
+
# Live metrics endpoint (while relay is running)
|
|
1120
|
+
curl http://localhost:8080/metrics
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
See [`benches/RESULTS.md`](benches/RESULTS.md) for full results with
|
|
1124
|
+
methodology and analysis.
|
|
1125
|
+
|
|
910
1126
|
## Features
|
|
911
1127
|
|
|
912
1128
|
| Feature | Default | Enables |
|
|
913
1129
|
|---------|---------|---------|
|
|
914
1130
|
| `webrtc` | No | `dep:str0m`, `dep:stun` — direct P2P connections via WebRTC data channels |
|
|
1131
|
+
| `fjall` | No | `dep:fjall` — LSM-tree storage backend (recommended for multi-node deployments) |
|
|
915
1132
|
| `persy` | No | `dep:persy` — Persy storage backend for high-concurrency workloads |
|
|
916
1133
|
|
|
917
|
-
Without `webrtc`, the `stun` module and `WebRtcPeer` adapter are stubbed out (functions return `None`). Without `persy`, the `PersyStorage` adapter is not compiled in and migration to/from Persy is unavailable.
|
|
1134
|
+
Without `webrtc`, the `stun` module and `WebRtcPeer` adapter are stubbed out (functions return `None`). Without `persy`, the `PersyStorage` adapter is not compiled in and migration to/from Persy is unavailable. Without `fjall`, the `FjallStorage` adapter is not compiled in and migration to/from fjall is unavailable. Migration requires at least one of `persy` or `fjall` features.
|
|
918
1135
|
|
|
919
|
-
**WASM**: When targeting `wasm32-unknown-unknown`, native-only modules (redb, Persy, tokio-tungstenite, multicast) are cfg-gated out. Browser adapters (`wasm_ws`, `wasm_idb`) are compiled in. Timer functions (`sleep`, `timeout`, `interval`) are provided by `tokio_with_wasm` via the `tokio_time` shim module instead of tokio's `time` feature (which panics on WASM). The `wasm.rs` module provides `#[wasm_bindgen]` JavaScript bindings. Build with `wasm-pack build --target web --release`.
|
|
1136
|
+
**WASM**: When targeting `wasm32-unknown-unknown`, native-only modules (redb, fjall, Persy, tokio-tungstenite, multicast) are cfg-gated out. Browser adapters (`wasm_ws`, `wasm_idb`) are compiled in. Timer functions (`sleep`, `timeout`, `interval`) are provided by `tokio_with_wasm` via the `tokio_time` shim module instead of tokio's `time` feature (which panics on WASM). The `wasm.rs` module provides `#[wasm_bindgen]` JavaScript bindings. Build with `wasm-pack build --target web --release`.
|
|
920
1137
|
|
|
921
1138
|
---
|
|
922
1139
|
|
package/beam.d.ts
CHANGED
|
@@ -49,6 +49,35 @@ export class Beam {
|
|
|
49
49
|
* then flushed automatically.
|
|
50
50
|
*/
|
|
51
51
|
static new_persistent(): Beam;
|
|
52
|
+
/**
|
|
53
|
+
* Creates a new BEAM node with OPFS (Origin Private File System) persistent storage.
|
|
54
|
+
*
|
|
55
|
+
* Data is stored as postcard-serialized files in the browser's OPFS
|
|
56
|
+
* and survives page reloads and browser restarts. Requires a secure
|
|
57
|
+
* context (HTTPS or localhost). OPFS is available in all modern browsers
|
|
58
|
+
* (Chrome 102+, Firefox 111+, Safari 15.2+).
|
|
59
|
+
*
|
|
60
|
+
* The OPFS directory opens asynchronously — writes are buffered until
|
|
61
|
+
* the directory is ready, then flushed automatically.
|
|
62
|
+
*
|
|
63
|
+
* ```js
|
|
64
|
+
* import init, { Beam } from "./beam.js";
|
|
65
|
+
* await init();
|
|
66
|
+
* const beam = Beam.new_with_opfs();
|
|
67
|
+
* beam.connect("ws://relay.example.com");
|
|
68
|
+
* beam.put("chat.001", "hello");
|
|
69
|
+
* // Reload page — data is still there.
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
static new_with_opfs(): Beam;
|
|
73
|
+
/**
|
|
74
|
+
* Creates a new BEAM node with OPFS storage at a custom directory name.
|
|
75
|
+
*
|
|
76
|
+
* ```js
|
|
77
|
+
* const beam = Beam.new_with_opfs_name("myapp_data");
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
static new_with_opfs_name(name: string): Beam;
|
|
52
81
|
/**
|
|
53
82
|
* Subscribes to child updates at the given path.
|
|
54
83
|
*
|
|
@@ -108,6 +137,8 @@ export interface InitOutput {
|
|
|
108
137
|
readonly beam_get: (a: number, b: number, c: number) => number;
|
|
109
138
|
readonly beam_new: () => number;
|
|
110
139
|
readonly beam_new_persistent: () => number;
|
|
140
|
+
readonly beam_new_with_opfs: () => number;
|
|
141
|
+
readonly beam_new_with_opfs_name: (a: number, b: number) => number;
|
|
111
142
|
readonly beam_on: (a: number, b: number, c: number, d: number) => void;
|
|
112
143
|
readonly beam_put: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
113
144
|
readonly beam_put_bool: (a: number, b: number, c: number, d: number) => void;
|
|
@@ -115,11 +146,11 @@ export interface InitOutput {
|
|
|
115
146
|
readonly beam_put_num: (a: number, b: number, c: number, d: number) => void;
|
|
116
147
|
readonly beam_stop: (a: number) => void;
|
|
117
148
|
readonly task_worker_entry_point: (a: number, b: number) => void;
|
|
118
|
-
readonly
|
|
119
|
-
readonly
|
|
120
|
-
readonly
|
|
121
|
-
readonly
|
|
122
|
-
readonly
|
|
149
|
+
readonly __wasm_bindgen_func_elem_1218: (a: number, b: number, c: number, d: number) => void;
|
|
150
|
+
readonly __wasm_bindgen_func_elem_1232: (a: number, b: number, c: number, d: number) => void;
|
|
151
|
+
readonly __wasm_bindgen_func_elem_252: (a: number, b: number, c: number) => void;
|
|
152
|
+
readonly __wasm_bindgen_func_elem_252_2: (a: number, b: number, c: number) => void;
|
|
153
|
+
readonly __wasm_bindgen_func_elem_252_3: (a: number, b: number, c: number) => void;
|
|
123
154
|
readonly __wbindgen_export: (a: number, b: number) => number;
|
|
124
155
|
readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
|
|
125
156
|
readonly __wbindgen_export3: (a: number) => void;
|
package/beam.js
CHANGED
|
@@ -83,6 +83,46 @@ export class Beam {
|
|
|
83
83
|
const ret = wasm.beam_new_persistent();
|
|
84
84
|
return Beam.__wrap(ret);
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Creates a new BEAM node with OPFS (Origin Private File System) persistent storage.
|
|
88
|
+
*
|
|
89
|
+
* Data is stored as postcard-serialized files in the browser's OPFS
|
|
90
|
+
* and survives page reloads and browser restarts. Requires a secure
|
|
91
|
+
* context (HTTPS or localhost). OPFS is available in all modern browsers
|
|
92
|
+
* (Chrome 102+, Firefox 111+, Safari 15.2+).
|
|
93
|
+
*
|
|
94
|
+
* The OPFS directory opens asynchronously — writes are buffered until
|
|
95
|
+
* the directory is ready, then flushed automatically.
|
|
96
|
+
*
|
|
97
|
+
* ```js
|
|
98
|
+
* import init, { Beam } from "./beam.js";
|
|
99
|
+
* await init();
|
|
100
|
+
* const beam = Beam.new_with_opfs();
|
|
101
|
+
* beam.connect("ws://relay.example.com");
|
|
102
|
+
* beam.put("chat.001", "hello");
|
|
103
|
+
* // Reload page — data is still there.
|
|
104
|
+
* ```
|
|
105
|
+
* @returns {Beam}
|
|
106
|
+
*/
|
|
107
|
+
static new_with_opfs() {
|
|
108
|
+
const ret = wasm.beam_new_with_opfs();
|
|
109
|
+
return Beam.__wrap(ret);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Creates a new BEAM node with OPFS storage at a custom directory name.
|
|
113
|
+
*
|
|
114
|
+
* ```js
|
|
115
|
+
* const beam = Beam.new_with_opfs_name("myapp_data");
|
|
116
|
+
* ```
|
|
117
|
+
* @param {string} name
|
|
118
|
+
* @returns {Beam}
|
|
119
|
+
*/
|
|
120
|
+
static new_with_opfs_name(name) {
|
|
121
|
+
const ptr0 = passStringToWasm0(name, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
122
|
+
const len0 = WASM_VECTOR_LEN;
|
|
123
|
+
const ret = wasm.beam_new_with_opfs_name(ptr0, len0);
|
|
124
|
+
return Beam.__wrap(ret);
|
|
125
|
+
}
|
|
86
126
|
/**
|
|
87
127
|
* Subscribes to child updates at the given path.
|
|
88
128
|
*
|
|
@@ -215,6 +255,10 @@ function __wbg_get_imports() {
|
|
|
215
255
|
__wbg__wbg_cb_unref_be22cc64ae6946a0: function(arg0) {
|
|
216
256
|
getObject(arg0)._wbg_cb_unref();
|
|
217
257
|
},
|
|
258
|
+
__wbg_arrayBuffer_393639d72165e90f: function(arg0) {
|
|
259
|
+
const ret = getObject(arg0).arrayBuffer();
|
|
260
|
+
return addHeapObject(ret);
|
|
261
|
+
},
|
|
218
262
|
__wbg_call_35dba3c747ad7521: function() { return handleError(function (arg0, arg1, arg2) {
|
|
219
263
|
const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));
|
|
220
264
|
return addHeapObject(ret);
|
|
@@ -222,10 +266,18 @@ function __wbg_get_imports() {
|
|
|
222
266
|
__wbg_close_b857478a8d4c1a16: function() { return handleError(function (arg0) {
|
|
223
267
|
getObject(arg0).close();
|
|
224
268
|
}, arguments); },
|
|
269
|
+
__wbg_close_bff888804a3b665e: function(arg0) {
|
|
270
|
+
const ret = getObject(arg0).close();
|
|
271
|
+
return addHeapObject(ret);
|
|
272
|
+
},
|
|
225
273
|
__wbg_createObjectStore_d3884936b845900f: function() { return handleError(function (arg0, arg1, arg2) {
|
|
226
274
|
const ret = getObject(arg0).createObjectStore(getStringFromWasm0(arg1, arg2));
|
|
227
275
|
return addHeapObject(ret);
|
|
228
276
|
}, arguments); },
|
|
277
|
+
__wbg_createWritable_376a22cc9862fab6: function(arg0) {
|
|
278
|
+
const ret = getObject(arg0).createWritable();
|
|
279
|
+
return addHeapObject(ret);
|
|
280
|
+
},
|
|
229
281
|
__wbg_data_57d8ce4eb5f0a433: function(arg0) {
|
|
230
282
|
const ret = getObject(arg0).data;
|
|
231
283
|
return addHeapObject(ret);
|
|
@@ -247,6 +299,26 @@ function __wbg_get_imports() {
|
|
|
247
299
|
__wbg_error_dd408a7b3cb542dd: function(arg0) {
|
|
248
300
|
console.error(getObject(arg0));
|
|
249
301
|
},
|
|
302
|
+
__wbg_getDirectoryHandle_3b6e6ec9cd618b74: function(arg0, arg1, arg2, arg3) {
|
|
303
|
+
const ret = getObject(arg0).getDirectoryHandle(getStringFromWasm0(arg1, arg2), getObject(arg3));
|
|
304
|
+
return addHeapObject(ret);
|
|
305
|
+
},
|
|
306
|
+
__wbg_getDirectory_2cc7169b6007ef84: function(arg0) {
|
|
307
|
+
const ret = getObject(arg0).getDirectory();
|
|
308
|
+
return addHeapObject(ret);
|
|
309
|
+
},
|
|
310
|
+
__wbg_getFileHandle_275e470e839818da: function(arg0, arg1, arg2) {
|
|
311
|
+
const ret = getObject(arg0).getFileHandle(getStringFromWasm0(arg1, arg2));
|
|
312
|
+
return addHeapObject(ret);
|
|
313
|
+
},
|
|
314
|
+
__wbg_getFileHandle_ba98be7045eab758: function(arg0, arg1, arg2, arg3) {
|
|
315
|
+
const ret = getObject(arg0).getFileHandle(getStringFromWasm0(arg1, arg2), getObject(arg3));
|
|
316
|
+
return addHeapObject(ret);
|
|
317
|
+
},
|
|
318
|
+
__wbg_getFile_f042da3b150a3230: function(arg0) {
|
|
319
|
+
const ret = getObject(arg0).getFile();
|
|
320
|
+
return addHeapObject(ret);
|
|
321
|
+
},
|
|
250
322
|
__wbg_getRandomValues_a608c4436c19407a: function() { return handleError(function (arg0, arg1) {
|
|
251
323
|
globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
|
|
252
324
|
}, arguments); },
|
|
@@ -272,9 +344,17 @@ function __wbg_get_imports() {
|
|
|
272
344
|
const ret = result;
|
|
273
345
|
return ret;
|
|
274
346
|
},
|
|
347
|
+
__wbg_length_36bd29c6848c2144: function(arg0) {
|
|
348
|
+
const ret = getObject(arg0).length;
|
|
349
|
+
return ret;
|
|
350
|
+
},
|
|
275
351
|
__wbg_log_e6372b4fbfc9f81e: function(arg0) {
|
|
276
352
|
console.log(getObject(arg0));
|
|
277
353
|
},
|
|
354
|
+
__wbg_navigator_6cfdd5fa246d910f: function(arg0) {
|
|
355
|
+
const ret = getObject(arg0).navigator;
|
|
356
|
+
return addHeapObject(ret);
|
|
357
|
+
},
|
|
278
358
|
__wbg_new_20a7c62e9b30cbf7: function() { return handleError(function (arg0, arg1) {
|
|
279
359
|
const ret = new WebSocket(getStringFromWasm0(arg0, arg1));
|
|
280
360
|
return addHeapObject(ret);
|
|
@@ -290,7 +370,7 @@ function __wbg_get_imports() {
|
|
|
290
370
|
const a = state0.a;
|
|
291
371
|
state0.a = 0;
|
|
292
372
|
try {
|
|
293
|
-
return
|
|
373
|
+
return __wasm_bindgen_func_elem_1232(a, state0.b, arg0, arg1);
|
|
294
374
|
} finally {
|
|
295
375
|
state0.a = a;
|
|
296
376
|
}
|
|
@@ -301,6 +381,14 @@ function __wbg_get_imports() {
|
|
|
301
381
|
state0.a = 0;
|
|
302
382
|
}
|
|
303
383
|
},
|
|
384
|
+
__wbg_new_77cc4f4f472aeb81: function(arg0) {
|
|
385
|
+
const ret = new Uint8Array(getObject(arg0));
|
|
386
|
+
return addHeapObject(ret);
|
|
387
|
+
},
|
|
388
|
+
__wbg_new_ebe3e0f6837f0879: function() {
|
|
389
|
+
const ret = new Object();
|
|
390
|
+
return addHeapObject(ret);
|
|
391
|
+
},
|
|
304
392
|
__wbg_new_typed_cceaf62d8d95e9f2: function(arg0, arg1) {
|
|
305
393
|
try {
|
|
306
394
|
var state0 = {a: arg0, b: arg1};
|
|
@@ -308,7 +396,7 @@ function __wbg_get_imports() {
|
|
|
308
396
|
const a = state0.a;
|
|
309
397
|
state0.a = 0;
|
|
310
398
|
try {
|
|
311
|
-
return
|
|
399
|
+
return __wasm_bindgen_func_elem_1232(a, state0.b, arg0, arg1);
|
|
312
400
|
} finally {
|
|
313
401
|
state0.a = a;
|
|
314
402
|
}
|
|
@@ -342,6 +430,9 @@ function __wbg_get_imports() {
|
|
|
342
430
|
__wbg_postMessage_6dcc1574fef77104: function() { return handleError(function (arg0, arg1) {
|
|
343
431
|
getObject(arg0).postMessage(getObject(arg1));
|
|
344
432
|
}, arguments); },
|
|
433
|
+
__wbg_prototypesetcall_de8e0d9553586985: function(arg0, arg1, arg2) {
|
|
434
|
+
Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), getObject(arg2));
|
|
435
|
+
},
|
|
345
436
|
__wbg_put_5e0ae8c80bb952a7: function() { return handleError(function (arg0, arg1, arg2) {
|
|
346
437
|
const ret = getObject(arg0).put(getObject(arg1), getObject(arg2));
|
|
347
438
|
return addHeapObject(ret);
|
|
@@ -353,10 +444,6 @@ function __wbg_get_imports() {
|
|
|
353
444
|
const ret = getObject(arg0).queueMicrotask;
|
|
354
445
|
return addHeapObject(ret);
|
|
355
446
|
},
|
|
356
|
-
__wbg_readyState_fe79161592fd15ce: function(arg0) {
|
|
357
|
-
const ret = getObject(arg0).readyState;
|
|
358
|
-
return ret;
|
|
359
|
-
},
|
|
360
447
|
__wbg_resolve_020f95d838c6ef25: function(arg0) {
|
|
361
448
|
const ret = Promise.resolve(getObject(arg0));
|
|
362
449
|
return addHeapObject(ret);
|
|
@@ -371,6 +458,12 @@ function __wbg_get_imports() {
|
|
|
371
458
|
__wbg_setTimeout_593504220b42c5a5: function(arg0, arg1) {
|
|
372
459
|
globalThis.setTimeout(getObject(arg0), arg1);
|
|
373
460
|
},
|
|
461
|
+
__wbg_set_create_7df81b728041e33b: function(arg0, arg1) {
|
|
462
|
+
getObject(arg0).create = arg1 !== 0;
|
|
463
|
+
},
|
|
464
|
+
__wbg_set_create_c415df73c1fec0ca: function(arg0, arg1) {
|
|
465
|
+
getObject(arg0).create = arg1 !== 0;
|
|
466
|
+
},
|
|
374
467
|
__wbg_set_onclose_cb71fea4ad9056fc: function(arg0, arg1) {
|
|
375
468
|
getObject(arg0).onclose = getObject(arg1);
|
|
376
469
|
},
|
|
@@ -415,6 +508,10 @@ function __wbg_get_imports() {
|
|
|
415
508
|
const ret = typeof window === 'undefined' ? null : window;
|
|
416
509
|
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
|
417
510
|
},
|
|
511
|
+
__wbg_storage_9c75a59b053f787c: function(arg0) {
|
|
512
|
+
const ret = getObject(arg0).storage;
|
|
513
|
+
return addHeapObject(ret);
|
|
514
|
+
},
|
|
418
515
|
__wbg_target_13424fe1cdc436ac: function(arg0) {
|
|
419
516
|
const ret = getObject(arg0).target;
|
|
420
517
|
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
|
@@ -431,24 +528,28 @@ function __wbg_get_imports() {
|
|
|
431
528
|
const ret = getObject(arg0).transaction(getStringFromWasm0(arg1, arg2), __wbindgen_enum_IdbTransactionMode[arg3]);
|
|
432
529
|
return addHeapObject(ret);
|
|
433
530
|
}, arguments); },
|
|
531
|
+
__wbg_write_6c58d60c26aaeef5: function() { return handleError(function (arg0, arg1, arg2) {
|
|
532
|
+
const ret = getObject(arg0).write(getArrayU8FromWasm0(arg1, arg2));
|
|
533
|
+
return addHeapObject(ret);
|
|
534
|
+
}, arguments); },
|
|
434
535
|
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
|
435
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx:
|
|
436
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
536
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 15, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
537
|
+
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_252);
|
|
437
538
|
return addHeapObject(ret);
|
|
438
539
|
},
|
|
439
540
|
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
|
440
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx:
|
|
441
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
541
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 290, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
|
542
|
+
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_1218);
|
|
442
543
|
return addHeapObject(ret);
|
|
443
544
|
},
|
|
444
545
|
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
|
445
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("IDBVersionChangeEvent")], shim_idx:
|
|
446
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
546
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("IDBVersionChangeEvent")], shim_idx: 15, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
547
|
+
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_252_2);
|
|
447
548
|
return addHeapObject(ret);
|
|
448
549
|
},
|
|
449
550
|
__wbindgen_cast_0000000000000004: function(arg0, arg1) {
|
|
450
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx:
|
|
451
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
551
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 15, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
552
|
+
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_252_3);
|
|
452
553
|
return addHeapObject(ret);
|
|
453
554
|
},
|
|
454
555
|
__wbindgen_cast_0000000000000005: function(arg0) {
|
|
@@ -475,22 +576,22 @@ function __wbg_get_imports() {
|
|
|
475
576
|
};
|
|
476
577
|
}
|
|
477
578
|
|
|
478
|
-
function
|
|
479
|
-
wasm.
|
|
579
|
+
function __wasm_bindgen_func_elem_252(arg0, arg1, arg2) {
|
|
580
|
+
wasm.__wasm_bindgen_func_elem_252(arg0, arg1, addHeapObject(arg2));
|
|
480
581
|
}
|
|
481
582
|
|
|
482
|
-
function
|
|
483
|
-
wasm.
|
|
583
|
+
function __wasm_bindgen_func_elem_252_2(arg0, arg1, arg2) {
|
|
584
|
+
wasm.__wasm_bindgen_func_elem_252_2(arg0, arg1, addHeapObject(arg2));
|
|
484
585
|
}
|
|
485
586
|
|
|
486
|
-
function
|
|
487
|
-
wasm.
|
|
587
|
+
function __wasm_bindgen_func_elem_252_3(arg0, arg1, arg2) {
|
|
588
|
+
wasm.__wasm_bindgen_func_elem_252_3(arg0, arg1, addHeapObject(arg2));
|
|
488
589
|
}
|
|
489
590
|
|
|
490
|
-
function
|
|
591
|
+
function __wasm_bindgen_func_elem_1218(arg0, arg1, arg2) {
|
|
491
592
|
try {
|
|
492
593
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
493
|
-
wasm.
|
|
594
|
+
wasm.__wasm_bindgen_func_elem_1218(retptr, arg0, arg1, addHeapObject(arg2));
|
|
494
595
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
495
596
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
496
597
|
if (r1) {
|
|
@@ -501,8 +602,8 @@ function __wasm_bindgen_func_elem_1101(arg0, arg1, arg2) {
|
|
|
501
602
|
}
|
|
502
603
|
}
|
|
503
604
|
|
|
504
|
-
function
|
|
505
|
-
wasm.
|
|
605
|
+
function __wasm_bindgen_func_elem_1232(arg0, arg1, arg2, arg3) {
|
|
606
|
+
wasm.__wasm_bindgen_func_elem_1232(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
|
|
506
607
|
}
|
|
507
608
|
|
|
508
609
|
|
package/beam_bg.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"David Newman <david.r.newman@proton.me>"
|
|
7
7
|
],
|
|
8
8
|
"description": "BEAM — distributed graph database syncing over WebSocket, WebRTC, and multicast. Successor to rod.",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.17.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|