@fairfox/polly 0.22.0 → 0.24.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.
Files changed (36) hide show
  1. package/README.md +55 -1
  2. package/dist/src/elysia/index.js +5 -3
  3. package/dist/src/elysia/index.js.map +3 -3
  4. package/dist/src/elysia/peer-repo-plugin.d.ts +1 -1
  5. package/dist/src/mesh-node.d.ts +89 -0
  6. package/dist/src/mesh-node.js +594 -0
  7. package/dist/src/mesh-node.js.map +14 -0
  8. package/dist/src/mesh.d.ts +10 -0
  9. package/dist/src/mesh.js +926 -24
  10. package/dist/src/mesh.js.map +17 -9
  11. package/dist/src/peer.d.ts +1 -0
  12. package/dist/src/peer.js +108 -85
  13. package/dist/src/peer.js.map +11 -10
  14. package/dist/src/shared/lib/blob-cache.d.ts +58 -0
  15. package/dist/src/shared/lib/blob-store-impl.d.ts +33 -0
  16. package/dist/src/shared/lib/blob-store.d.ts +87 -0
  17. package/dist/src/shared/lib/blob-transfer.d.ts +58 -0
  18. package/dist/src/shared/lib/crdt-specialised.d.ts +1 -1
  19. package/dist/src/shared/lib/crdt-state.d.ts +1 -1
  20. package/dist/src/shared/lib/keyring-storage.d.ts +57 -0
  21. package/dist/src/shared/lib/mesh-client.d.ts +91 -0
  22. package/dist/src/shared/lib/mesh-network-adapter.d.ts +1 -1
  23. package/dist/src/shared/lib/mesh-signaling-client.d.ts +6 -0
  24. package/dist/src/shared/lib/mesh-state.d.ts +1 -1
  25. package/dist/src/shared/lib/mesh-webrtc-adapter.d.ts +20 -1
  26. package/dist/src/shared/lib/peer-relay-adapter.d.ts +1 -1
  27. package/dist/src/shared/lib/peer-repo-server.d.ts +1 -1
  28. package/dist/src/shared/lib/peer-state.d.ts +1 -1
  29. package/dist/src/shared/lib/wasm-init.d.ts +17 -0
  30. package/dist/tools/quality/src/cli.js +98 -47
  31. package/dist/tools/quality/src/cli.js.map +4 -4
  32. package/dist/tools/quality/src/index.d.ts +25 -0
  33. package/dist/tools/quality/src/index.js +196 -0
  34. package/dist/tools/quality/src/index.js.map +10 -0
  35. package/dist/tools/quality/src/no-as-casting.d.ts +44 -0
  36. package/package.json +22 -2
@@ -0,0 +1,33 @@
1
+ /**
2
+ * blob-store-impl — peer-to-peer blob store backed by WebRTC data channels.
3
+ *
4
+ * The store piggybacks on an existing MeshWebRTCAdapter. Blob messages ride
5
+ * on the same data channel as Automerge sync traffic, distinguished by a
6
+ * "blob-" prefix on the message type field.
7
+ *
8
+ * Lifecycle:
9
+ * - put: hash-verify → cache plaintext locally → announce blob-have
10
+ * - get: check cache → if miss, send blob-request(s) → receive chunks
11
+ * → decrypt each chunk as it arrives → reassemble plaintext
12
+ * → hash-verify → cache
13
+ *
14
+ * Encryption model: chunk-then-encrypt. The sender chunks the plaintext
15
+ * into 64 KiB pieces, encrypts each chunk independently under the configured
16
+ * key (with a fresh random nonce per chunk), and sends the sealed envelope.
17
+ * The receiver decrypts each chunk on arrival. Peak sender memory is ~1
18
+ * chunk of ciphertext at a time rather than the entire ciphertext blob.
19
+ *
20
+ * Per-op keys: callers can override the store's default encryption key per
21
+ * put/get via BlobTransferOptions.key. This supports per-document keys
22
+ * without requiring the store to track document ownership.
23
+ *
24
+ * Multi-source fetch: the get() flow requests from a single peer initially,
25
+ * but the re-request timer rotates through peers that have announced
26
+ * availability of the blob, so a transfer that stalls on one peer recovers
27
+ * against another.
28
+ */
29
+ import type { BlobStore, BlobStoreOptions } from "./blob-store";
30
+ import type { MeshWebRTCAdapter } from "./mesh-webrtc-adapter";
31
+ /** Create a blob store that transfers blobs peer-to-peer over a
32
+ * MeshWebRTCAdapter's data channels. */
33
+ export declare function createBlobStore(adapter: MeshWebRTCAdapter, options?: BlobStoreOptions): BlobStore;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * blob-store — types and interfaces for Polly's content-addressed blob storage.
3
+ *
4
+ * This file contains only type definitions and interfaces. It has no runtime
5
+ * imports from tweetnacl or other crypto libraries, so peer-only consumers
6
+ * tree-shake cleanly when importing from @fairfox/polly/mesh.
7
+ */
8
+ import type { BlobRef } from "./blob-ref";
9
+ export type BlobProgressPhase = "encrypting" | "uploading" | "downloading" | "decrypting";
10
+ export interface BlobProgressEvent {
11
+ /** Bytes processed so far in the current phase. */
12
+ loaded: number;
13
+ /** Total bytes expected. Undefined when unknown (e.g. start of download). */
14
+ total: number | undefined;
15
+ /** Current operation phase. */
16
+ phase: BlobProgressPhase;
17
+ }
18
+ export type BlobProgressCallback = (event: BlobProgressEvent) => void;
19
+ export interface BlobTransferOptions {
20
+ onProgress?: BlobProgressCallback;
21
+ signal?: AbortSignal;
22
+ /** Override the store's default encryption key for this operation.
23
+ * Useful for per-document keys: pass the document's key here so the
24
+ * blob is encrypted under that key rather than the store default.
25
+ * Ignored if the store has no encryption configured. */
26
+ key?: Uint8Array;
27
+ }
28
+ /** Storage backend for blob bytes. Implementations must store Uint8Array
29
+ * values keyed by SHA-256 hex hash without serialisation loss. */
30
+ export interface BlobCache {
31
+ get(hash: string): Promise<Uint8Array | undefined>;
32
+ put(hash: string, bytes: Uint8Array): Promise<void>;
33
+ has(hash: string): Promise<boolean>;
34
+ delete(hash: string): Promise<void>;
35
+ dispose(): void;
36
+ /** Mark a hash as pinned — it will not be evicted until unpinned.
37
+ * Applications typically pin blobs referenced by documents they
38
+ * currently hold locally. */
39
+ pin(hash: string): Promise<void>;
40
+ /** Remove the pinned mark. The blob becomes eligible for eviction. */
41
+ unpin(hash: string): Promise<void>;
42
+ /** Total bytes stored in the cache. */
43
+ size(): Promise<number>;
44
+ /** Evict unpinned entries in LRU order until total size ≤ maxBytes.
45
+ * Returns the number of bytes freed. */
46
+ evict(maxBytes: number): Promise<number>;
47
+ }
48
+ export interface BlobStore {
49
+ /** Store bytes locally and announce availability to connected peers.
50
+ * Verifies that the hash of `bytes` matches `ref.hash` before storing. */
51
+ put(ref: BlobRef, bytes: Uint8Array, options?: BlobTransferOptions): Promise<void>;
52
+ /** Retrieve bytes by hash. Checks local cache first; if not cached,
53
+ * requests from connected peers. Returns undefined if unavailable. */
54
+ get(hash: string, options?: BlobTransferOptions): Promise<Uint8Array | undefined>;
55
+ /** Return an object URL for rendering (e.g. <img src>). Cached per hash;
56
+ * repeated calls with the same hash return the same URL. Returns
57
+ * undefined if the blob is not in the local cache. All URLs are revoked
58
+ * on dispose(). */
59
+ url(hash: string): Promise<string | undefined>;
60
+ /** Pin a blob so it won't be evicted. Applications should pin blobs
61
+ * referenced by documents they currently hold. */
62
+ pin(hash: string): Promise<void>;
63
+ /** Unpin a blob. Removing a document that references a blob is a
64
+ * natural place to unpin. */
65
+ unpin(hash: string): Promise<void>;
66
+ /** Total bytes stored in the local cache. */
67
+ size(): Promise<number>;
68
+ /** Evict unpinned entries in LRU order until total size ≤ maxBytes.
69
+ * Returns bytes freed. */
70
+ evict(maxBytes: number): Promise<number>;
71
+ /** Revoke all outstanding object URLs and release resources. */
72
+ dispose(): void;
73
+ }
74
+ export interface BlobStoreOptions {
75
+ /** Maximum blob size in bytes. Defaults to 100 MiB. */
76
+ maxBlobSize?: number;
77
+ /** Default encryption config. When configured, blobs are encrypted
78
+ * with this key unless overridden per-op via BlobTransferOptions.key.
79
+ * Omit for plaintext transfer (DTLS only). */
80
+ encrypt?: {
81
+ key: Uint8Array;
82
+ };
83
+ /** Custom cache implementation. Defaults to MemoryBlobCache (suitable
84
+ * for tests and Node). Browser consumers should pass an
85
+ * IndexedDBBlobCache instance. */
86
+ cache?: BlobCache;
87
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * blob-transfer — chunking, reassembly, and wire format for peer-to-peer
3
+ * blob transfer over WebRTC data channels.
4
+ *
5
+ * Blobs are split into fixed-size chunks (64 KiB) and sent as individual
6
+ * data channel messages. The wire format reuses the same length-prefixed
7
+ * header + binary payload layout that MeshWebRTCAdapter uses for Automerge
8
+ * sync messages. Blob messages are distinguished by a `type` field in the
9
+ * JSON header that starts with "blob-".
10
+ *
11
+ * Three message types:
12
+ * - blob-chunk: a single chunk of a blob transfer
13
+ * - blob-request: ask a peer to send chunks for a specific hash
14
+ * - blob-have: announce local availability of a blob
15
+ */
16
+ /** Default chunk size in bytes: 64 KiB. Stays well within WebRTC SCTP
17
+ * message size limits (~256 KiB in most browsers). */
18
+ export declare const BLOB_CHUNK_SIZE = 65536;
19
+ /** High-water mark for RTCDataChannel.bufferedAmount. The sender pauses
20
+ * when the buffer exceeds this threshold. */
21
+ export declare const BLOB_BUFFER_HIGH_WATER: number;
22
+ export interface BlobChunkHeader {
23
+ type: "blob-chunk";
24
+ hash: string;
25
+ index: number;
26
+ total: number;
27
+ }
28
+ export interface BlobRequestHeader {
29
+ type: "blob-request";
30
+ hash: string;
31
+ /** Chunk indices needed. Omit to request all chunks. */
32
+ missing?: number[];
33
+ }
34
+ export interface BlobHaveHeader {
35
+ type: "blob-have";
36
+ hash: string;
37
+ }
38
+ export type BlobMessageHeader = BlobChunkHeader | BlobRequestHeader | BlobHaveHeader;
39
+ /** Split bytes into fixed-size chunks. The last chunk may be smaller. */
40
+ export declare function chunkBlob(bytes: Uint8Array, chunkSize?: number): Uint8Array[];
41
+ /** Reassemble chunks into a single Uint8Array. Throws if any chunk index
42
+ * in [0, total) is missing from the map. */
43
+ export declare function reassembleChunks(chunks: Map<number, Uint8Array>, total: number): Uint8Array;
44
+ /** Return the indices of chunks missing from a partial chunk map. */
45
+ export declare function missingChunkIndices(chunks: Map<number, Uint8Array>, total: number): number[];
46
+ /** Serialise a blob message into the shared wire format:
47
+ * [4-byte BE header length][JSON header bytes][binary payload].
48
+ * Returns an ArrayBuffer-backed Uint8Array usable by RTCDataChannel.send. */
49
+ export declare function serialiseBlobMessage(header: BlobMessageHeader, data?: Uint8Array): Uint8Array<ArrayBuffer>;
50
+ /** Peek at the header of a wire-format message without full
51
+ * deserialisation. Returns the parsed header and the data slice,
52
+ * or undefined if the bytes are too short or malformed. */
53
+ export declare function parseBlobMessage(bytes: Uint8Array): {
54
+ header: BlobMessageHeader;
55
+ data: Uint8Array;
56
+ } | undefined;
57
+ /** Check whether a wire-format message header type starts with "blob-". */
58
+ export declare function isBlobMessageType(bytes: Uint8Array): boolean;
@@ -33,7 +33,7 @@
33
33
  * and the {@link MigratableState} interface. Variants differ only in the
34
34
  * `extractValue` and `applyWrite` hooks they pass.
35
35
  */
36
- import { Counter, type DocHandle } from "@automerge/automerge-repo";
36
+ import { Counter, type DocHandle } from "@automerge/automerge-repo/slim";
37
37
  import type { Access } from "./access";
38
38
  import { type MigratableState } from "./migrate-primitive";
39
39
  import { type PrimitiveKind } from "./primitive-registry";
@@ -22,7 +22,7 @@
22
22
  * counters, and lists (which require type-specific operation capture to
23
23
  * preserve concurrent-edit semantics) land in Phase 1's crdt-specialised.ts.
24
24
  */
25
- import type { DocHandle } from "@automerge/automerge-repo";
25
+ import type { DocHandle } from "@automerge/automerge-repo/slim";
26
26
  import type { Access } from "./access";
27
27
  import { type MigratableState } from "./migrate-primitive";
28
28
  import { type PrimitiveKind } from "./primitive-registry";
@@ -0,0 +1,57 @@
1
+ /**
2
+ * keyring-storage — persistence abstraction for {@link MeshKeyring}.
3
+ *
4
+ * The keyring itself is a plain structural object of `Map`s, `Set`s, and a
5
+ * signing keypair; it is deliberately not coupled to any persistence layer.
6
+ * This module defines a storage interface that applications implement once
7
+ * for their runtime (IndexedDB, the filesystem, a keychain, a secret
8
+ * manager, whatever) and wire into {@link createMeshClient} via its
9
+ * `keyring.storage` option.
10
+ *
11
+ * A canonical JSON-with-base64 serialisation is provided by
12
+ * {@link serialiseKeyring} and {@link deserialiseKeyring}. It is inspectable
13
+ * by humans, survives manual edits, and round-trips every field of the
14
+ * keyring. Storage implementations that write plain strings (files,
15
+ * localStorage, `kv` stores) can lean on these helpers; storage
16
+ * implementations that persist structured data (IndexedDB, a keychain API)
17
+ * can serialise differently if they prefer.
18
+ */
19
+ import type { MeshKeyring } from "./mesh-network-adapter";
20
+ /**
21
+ * A load/save pair for a single {@link MeshKeyring}. Implementations are
22
+ * free to choose where and how the keyring is stored; the factory only
23
+ * cares that `load()` returns the previously-saved keyring or `null`, and
24
+ * that `save(keyring)` durably persists it.
25
+ */
26
+ export interface KeyringStorage {
27
+ /** Load the previously-saved keyring, or return `null` if none exists.
28
+ * Implementations may throw for truly exceptional conditions (disk
29
+ * errors, permission failures); a missing keyring is not exceptional. */
30
+ load(): Promise<MeshKeyring | null>;
31
+ /** Durably persist the keyring. Implementations should atomically replace
32
+ * any existing stored value; partial writes must not leave the store in
33
+ * an inconsistent state. */
34
+ save(keyring: MeshKeyring): Promise<void>;
35
+ }
36
+ /**
37
+ * In-memory storage. Useful for tests, ephemeral tools, and the first-run
38
+ * bootstrap path where the keyring only lives for the duration of the
39
+ * process. Calling `save` holds the keyring in a closed-over variable;
40
+ * `load` returns it on subsequent calls within the same process.
41
+ */
42
+ export declare function memoryKeyringStorage(): KeyringStorage;
43
+ /**
44
+ * Encode a {@link MeshKeyring} to a canonical JSON string. Every
45
+ * `Uint8Array` field (identity keys, public keys, document keys) is
46
+ * base64-encoded; `Map`s and `Set`s become plain objects and arrays. The
47
+ * output is pretty-printed so a human operator can eyeball or hand-edit
48
+ * the file on disk.
49
+ */
50
+ export declare function serialiseKeyring(keyring: MeshKeyring): string;
51
+ /**
52
+ * Decode a keyring from the format produced by {@link serialiseKeyring}.
53
+ * Throws with a descriptive message when the input is malformed, so
54
+ * corrupt storage surfaces as an actionable error rather than a silent
55
+ * downgrade.
56
+ */
57
+ export declare function deserialiseKeyring(text: string): MeshKeyring;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * mesh-client — first-class factory for constructing a Polly mesh client.
3
+ *
4
+ * The mesh transport stack has five pieces that have to be wired together:
5
+ * a {@link MeshSignalingClient} talking to the relay server, a
6
+ * {@link MeshWebRTCAdapter} that owns the per-peer RTCPeerConnections, a
7
+ * {@link MeshNetworkAdapter} that signs and encrypts every message on the
8
+ * way through, an Automerge `Repo` that drives sync, and a `MeshKeyring`
9
+ * that holds the crypto material. Prior to this module, every consuming
10
+ * application had to assemble the five pieces by hand — and in Node or
11
+ * Bun had to monkey-patch `globalThis.WebSocket` / `globalThis.RTCPeerConnection`
12
+ * because the lower-level primitives reached for those globals.
13
+ *
14
+ * `createMeshClient` takes options, hands back a `MeshClient`, and also
15
+ * calls `configureMeshState(client.repo)` so `$meshState` works without
16
+ * a second setup step. The WebSocket and RTCPeerConnection implementations
17
+ * are injectable; defaults read from `globalThis` for browser ergonomics.
18
+ * The companion `@fairfox/polly/mesh/node` subpath provides a CLI bootstrap
19
+ * helper that wires werift (or `@roamhq/wrtc` if the consumer prefers) and
20
+ * a file-backed keyring store.
21
+ */
22
+ import { Repo, type StorageAdapterInterface } from "@automerge/automerge-repo/slim";
23
+ import type { KeyringStorage } from "./keyring-storage";
24
+ import { type MeshKeyring, MeshNetworkAdapter } from "./mesh-network-adapter";
25
+ import { MeshSignalingClient, type MeshSignalingClientOptions } from "./mesh-signaling-client";
26
+ import { MeshWebRTCAdapter, type MeshWebRTCAdapterOptions } from "./mesh-webrtc-adapter";
27
+ /** Options for {@link createMeshClient}. */
28
+ export interface CreateMeshClientOptions {
29
+ /** Signalling-server configuration. `peerId` must be the same identity
30
+ * this client's keyring was paired with on other peers. */
31
+ signaling: {
32
+ url: string;
33
+ peerId: string;
34
+ /** Optional WebSocket ctor override (e.g. `ws` on old Node). Defaults
35
+ * to `globalThis.WebSocket`. */
36
+ WebSocket?: MeshSignalingClientOptions["WebSocket"];
37
+ /** Forwarded error callback for diagnostic UI. */
38
+ onError?: MeshSignalingClientOptions["onError"];
39
+ };
40
+ /** WebRTC configuration. On browsers the defaults are fine; in Node or
41
+ * Bun pass the `RTCPeerConnection` ctor from `werift` or `@roamhq/wrtc`. */
42
+ rtc?: {
43
+ RTCPeerConnection?: MeshWebRTCAdapterOptions["RTCPeerConnection"];
44
+ iceServers?: RTCIceServer[];
45
+ dataChannelLabel?: string;
46
+ };
47
+ /** The local peer's keyring — either a fully-constructed instance, or a
48
+ * persistence adapter to load one from. When a storage adapter is given
49
+ * and `storage.load()` resolves to `null`, the factory throws with a
50
+ * message pointing at the bootstrap helper in `@fairfox/polly/mesh/node`;
51
+ * we deliberately do not generate an identity silently. */
52
+ keyring: MeshKeyring | {
53
+ storage: KeyringStorage;
54
+ };
55
+ /** Optional Automerge-Repo storage adapter. Applications that want
56
+ * durable local state pass an IndexedDB adapter in browsers or a
57
+ * filesystem adapter in Node; omitting it keeps the Repo in-memory. */
58
+ repoStorage?: StorageAdapterInterface;
59
+ /** When `false`, signs but does not encrypt. Defaults to `true` — the
60
+ * full $meshState posture where the server is off the data path. */
61
+ encryptionEnabled?: boolean;
62
+ }
63
+ /** Handle returned by {@link createMeshClient}. */
64
+ export interface MeshClient {
65
+ /** The Automerge Repo. `$meshState` has already been configured against
66
+ * this repo, so primitives just work — but the repo is exposed in case
67
+ * the application needs it directly (server-side cron, bulk exports,
68
+ * migration tools). */
69
+ repo: Repo;
70
+ /** The configured keyring. Exposed so the application can inspect or
71
+ * mutate it (add authorised peers, apply revocations) and then
72
+ * re-persist via its storage. */
73
+ keyring: MeshKeyring;
74
+ /** The signalling client. Exposed for applications that need to hook
75
+ * lifecycle events or send custom signalling payloads. */
76
+ signaling: MeshSignalingClient;
77
+ /** The WebRTC network adapter. Exposed for advanced use (blob store
78
+ * wiring, peer-connection introspection). */
79
+ networkAdapter: MeshNetworkAdapter;
80
+ /** The underlying WebRTC adapter wrapped by {@link networkAdapter}. */
81
+ webrtcAdapter: MeshWebRTCAdapter;
82
+ /** Close the signalling WebSocket, tear down every RTCPeerConnection,
83
+ * and shut the Repo cleanly. Idempotent. */
84
+ close(): Promise<void>;
85
+ }
86
+ /**
87
+ * Construct a fully-wired mesh client. Resolves once the signalling
88
+ * connection is open and the Repo is ready to mutate documents; WebRTC
89
+ * peer connections negotiate asynchronously in the background.
90
+ */
91
+ export declare function createMeshClient(options: CreateMeshClientOptions): Promise<MeshClient>;
@@ -39,7 +39,7 @@
39
39
  * threading the document context through the network subsystem (which
40
40
  * needs upstream support). A follow-up will address this.
41
41
  */
42
- import { type Message, NetworkAdapter, type PeerId, type PeerMetadata } from "@automerge/automerge-repo";
42
+ import { type Message, NetworkAdapter, type PeerId, type PeerMetadata } from "@automerge/automerge-repo/slim";
43
43
  import { type SigningKeyPair } from "./signing";
44
44
  /** The well-known document id used for the Phase 2 first-cut single-key
45
45
  * encryption mode. See the file-level comment for the per-document key
@@ -41,6 +41,11 @@ export interface MeshSignalingClientOptions {
41
41
  /** Optional callback for the open and close lifecycle events. */
42
42
  onOpen?: () => void;
43
43
  onClose?: () => void;
44
+ /** WebSocket constructor. Defaults to `globalThis.WebSocket`. Inject a
45
+ * different implementation (e.g. `ws` package's `WebSocket`) when running
46
+ * in an environment without a native WebSocket global, or to use a custom
47
+ * subclass for tests or instrumentation. */
48
+ WebSocket?: typeof WebSocket;
44
49
  }
45
50
  /**
46
51
  * Thin wrapper around a WebSocket connection to a Polly signalling server.
@@ -61,6 +66,7 @@ export declare class MeshSignalingClient {
61
66
  private readonly onClose?;
62
67
  private socket;
63
68
  private joined;
69
+ private readonly WebSocketCtor;
64
70
  constructor(options: MeshSignalingClientOptions);
65
71
  /**
66
72
  * Open the WebSocket and send the join message. Resolves once the
@@ -32,7 +32,7 @@
32
32
  * RTCDataChannel; for tests and for the early Phase 2 cut, an in-memory
33
33
  * loopback adapter pair satisfies the same contract.
34
34
  */
35
- import type { Repo } from "@automerge/automerge-repo";
35
+ import type { Repo } from "@automerge/automerge-repo/slim";
36
36
  import type { Access } from "./access";
37
37
  import { type SpecialisedPrimitive } from "./crdt-specialised";
38
38
  import { type CrdtPrimitive } from "./crdt-state";
@@ -44,7 +44,7 @@
44
44
  * - Disconnect tears down every peer connection and closes the
45
45
  * signalling client.
46
46
  */
47
- import { type Message, NetworkAdapter, type PeerId, type PeerMetadata } from "@automerge/automerge-repo";
47
+ import { type Message, NetworkAdapter, type PeerId, type PeerMetadata } from "@automerge/automerge-repo/slim";
48
48
  import type { MeshSignalingClient } from "./mesh-signaling-client";
49
49
  /** Standard STUN servers for NAT traversal. In production, callers who
50
50
  * need TURN fallback for peers behind symmetric NATs should replace this
@@ -72,6 +72,11 @@ export interface MeshWebRTCAdapterOptions {
72
72
  * that share a signalling server between multiple meshes may want
73
73
  * distinct labels per mesh. */
74
74
  dataChannelLabel?: string;
75
+ /** RTCPeerConnection constructor. Defaults to
76
+ * `globalThis.RTCPeerConnection`. Inject a different implementation
77
+ * (e.g. `werift` or `@roamhq/wrtc`) when running outside a browser, or
78
+ * to use a custom subclass for tests or instrumentation. */
79
+ RTCPeerConnection?: typeof RTCPeerConnection;
75
80
  }
76
81
  /**
77
82
  * Automerge-Repo NetworkAdapter backed by real WebRTC data channels.
@@ -83,9 +88,14 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
83
88
  readonly iceServers: RTCIceServer[];
84
89
  readonly dataChannelLabel: string;
85
90
  readonly knownPeerIds: string[];
91
+ private readonly RTCPeerConnectionCtor;
86
92
  private readonly slots;
87
93
  private ready;
88
94
  private readyResolver;
95
+ /** Callback for incoming blob messages. Set by the blob store.
96
+ * Called with the sender's peer ID, the raw header object, and the
97
+ * binary payload (chunk data). */
98
+ onBlobMessage?: (peerId: string, header: Record<string, unknown>, data: Uint8Array) => void;
89
99
  constructor(options: MeshWebRTCAdapterOptions);
90
100
  isReady(): boolean;
91
101
  whenReady(): Promise<void>;
@@ -120,6 +130,15 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
120
130
  private wireConnection;
121
131
  private wireDataChannel;
122
132
  private dispatchMessage;
133
+ /** Peer IDs with an open data channel, suitable for blob requests. */
134
+ get connectedPeerIds(): string[];
135
+ /** Send a pre-serialised blob message to a specific peer. Returns false
136
+ * if the peer is not connected or the send buffer is above the high-water
137
+ * mark (caller should retry after a delay). */
138
+ sendBlobMessage(peerId: string, bytes: Uint8Array<ArrayBuffer>): boolean;
139
+ /** Send bytes on a data channel if the buffer is below the high-water
140
+ * mark. Returns true if sent, false if backpressure applies. */
141
+ private trySendOnChannel;
123
142
  /** Pack an Automerge Message into binary for transmission over the
124
143
  * data channel. The format mirrors MeshNetworkAdapter's internal
125
144
  * serialisation: a length-prefixed JSON header followed by the raw
@@ -25,7 +25,7 @@
25
25
  * // connectionState is a Signal<"connecting" | "connected" | "disconnected">
26
26
  * ```
27
27
  */
28
- import { Repo } from "@automerge/automerge-repo";
28
+ import { Repo } from "@automerge/automerge-repo/slim";
29
29
  import { WebSocketClientAdapter } from "@automerge/automerge-repo-network-websocket";
30
30
  import { type Signal } from "@preact/signals";
31
31
  import { type MeshKeyring } from "./mesh-network-adapter";
@@ -34,7 +34,7 @@
34
34
  * await server.close();
35
35
  * ```
36
36
  */
37
- import { Repo } from "@automerge/automerge-repo";
37
+ import { Repo } from "@automerge/automerge-repo/slim";
38
38
  import { WebSocketServerAdapter } from "@automerge/automerge-repo-network-websocket";
39
39
  import { NodeFSStorageAdapter } from "@automerge/automerge-repo-storage-nodefs";
40
40
  import * as ws from "ws";
@@ -33,7 +33,7 @@
33
33
  * via the same configuration path; Phase 2's mesh adapter does the same for
34
34
  * $meshState.
35
35
  */
36
- import type { Repo } from "@automerge/automerge-repo";
36
+ import type { Repo } from "@automerge/automerge-repo/slim";
37
37
  import type { Access } from "./access";
38
38
  import { type SpecialisedPrimitive } from "./crdt-specialised";
39
39
  import { type CrdtPrimitive } from "./crdt-state";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Eagerly initialise Automerge's WebAssembly runtime by pointing the library
3
+ * at the raw `.wasm` file shipped in `@automerge/automerge`.
4
+ *
5
+ * The `import.meta.url` pattern below is recognised by every modern bundler
6
+ * (Bun, Vite, webpack 5+, esbuild) as an asset reference: the `.wasm` gets
7
+ * copied into the consumer's output alongside the JavaScript, and the URL is
8
+ * rewritten to point at the copied file. `initializeWasm` then fetches and
9
+ * instantiates it at runtime. That keeps the WebAssembly out of the JavaScript
10
+ * bundle entirely — no megabyte-scale base64 string inlined per subpath — and
11
+ * lets the browser parse the JS and stream-compile the `.wasm` in parallel.
12
+ *
13
+ * The top-level `await` makes this module async so any consumer that
14
+ * transitively imports it inherits the "wait for WASM" behaviour through the
15
+ * import graph, without needing an explicit init call.
16
+ */
17
+ export {};