@fairfox/polly 0.23.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.
- package/README.md +55 -1
- package/dist/src/elysia/index.js +2 -2
- package/dist/src/elysia/index.js.map +2 -2
- package/dist/src/elysia/peer-repo-plugin.d.ts +1 -1
- package/dist/src/mesh-node.d.ts +89 -0
- package/dist/src/mesh-node.js +594 -0
- package/dist/src/mesh-node.js.map +14 -0
- package/dist/src/mesh.d.ts +10 -0
- package/dist/src/mesh.js +926 -24
- package/dist/src/mesh.js.map +17 -9
- package/dist/src/peer.d.ts +1 -0
- package/dist/src/peer.js +105 -84
- package/dist/src/peer.js.map +11 -10
- package/dist/src/shared/lib/blob-cache.d.ts +58 -0
- package/dist/src/shared/lib/blob-store-impl.d.ts +33 -0
- package/dist/src/shared/lib/blob-store.d.ts +87 -0
- package/dist/src/shared/lib/blob-transfer.d.ts +58 -0
- package/dist/src/shared/lib/crdt-specialised.d.ts +1 -1
- package/dist/src/shared/lib/crdt-state.d.ts +1 -1
- package/dist/src/shared/lib/keyring-storage.d.ts +57 -0
- package/dist/src/shared/lib/mesh-client.d.ts +91 -0
- package/dist/src/shared/lib/mesh-network-adapter.d.ts +1 -1
- package/dist/src/shared/lib/mesh-signaling-client.d.ts +6 -0
- package/dist/src/shared/lib/mesh-state.d.ts +1 -1
- package/dist/src/shared/lib/mesh-webrtc-adapter.d.ts +20 -1
- package/dist/src/shared/lib/peer-relay-adapter.d.ts +1 -1
- package/dist/src/shared/lib/peer-repo-server.d.ts +1 -1
- package/dist/src/shared/lib/peer-state.d.ts +1 -1
- package/dist/src/shared/lib/wasm-init.d.ts +17 -0
- package/dist/tools/quality/src/cli.js +8 -1
- package/dist/tools/quality/src/cli.js.map +3 -3
- package/dist/tools/quality/src/index.d.ts +25 -0
- package/dist/tools/quality/src/index.js +196 -0
- package/dist/tools/quality/src/index.js.map +10 -0
- package/dist/tools/quality/src/no-as-casting.d.ts +44 -0
- package/package.json +22 -2
package/dist/src/mesh.js.map
CHANGED
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/shared/lib/encryption.ts", "../src/shared/lib/mesh-network-adapter.ts", "../src/shared/lib/signing.ts", "../src/shared/lib/mesh-signaling-client.ts", "../src/shared/lib/crdt-specialised.ts", "../src/shared/lib/migrate-primitive.ts", "../src/shared/lib/primitive-registry.ts", "../src/shared/lib/schema-version.ts", "../src/shared/lib/crdt-state.ts", "../src/shared/lib/mesh-state.ts", "../src/shared/lib/mesh-webrtc-adapter.ts", "../src/shared/lib/pairing.ts", "../src/shared/lib/revocation.ts"],
|
|
3
|
+
"sources": ["../src/shared/lib/encryption.ts", "../src/shared/lib/wasm-init.ts", "../src/shared/lib/blob-cache.ts", "../src/shared/lib/blob-ref.ts", "../src/shared/lib/blob-transfer.ts", "../src/shared/lib/blob-store-impl.ts", "../src/mesh.ts", "../src/shared/lib/keyring-storage.ts", "../src/shared/lib/mesh-client.ts", "../src/shared/lib/mesh-network-adapter.ts", "../src/shared/lib/signing.ts", "../src/shared/lib/mesh-signaling-client.ts", "../src/shared/lib/crdt-specialised.ts", "../src/shared/lib/migrate-primitive.ts", "../src/shared/lib/primitive-registry.ts", "../src/shared/lib/schema-version.ts", "../src/shared/lib/crdt-state.ts", "../src/shared/lib/mesh-state.ts", "../src/shared/lib/mesh-webrtc-adapter.ts", "../src/shared/lib/pairing.ts", "../src/shared/lib/revocation.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * encryption — symmetric authenticated encryption for Polly's $meshState\n * primitive (Phase 2). Wraps tweetnacl's secretbox (XSalsa20-Poly1305) with\n * a small Polly-flavoured API so the rest of the codebase never imports\n * tweetnacl directly.\n *\n * Every $meshState document has a per-document symmetric key that is\n * provisioned to authorised peers at pairing time and never held by any\n * server. Outgoing operations are encrypted under this key before they\n * touch the network adapter; incoming operations are decrypted on receipt.\n * The signing layer in {@link signing.ts} provides authenticity (proof of\n * who sent the message); this layer provides confidentiality (the bytes\n * are unreadable to anything that does not hold the document key).\n *\n * tweetnacl's secretbox uses a 32-byte symmetric key and a 24-byte nonce.\n * The output of `nacl.secretbox` is the ciphertext concatenated with a\n * 16-byte Poly1305 authentication tag. We package the nonce + ciphertext\n * into a single binary blob using a small length-prefixed envelope so the\n * receiver can recover the nonce without out-of-band coordination.\n *\n * - {@link generateDocumentKey} returns a fresh 32-byte symmetric key.\n *\n * - {@link encrypt} produces a sealed blob from a payload and a key.\n *\n * - {@link decrypt} recovers the payload from a sealed blob and a key.\n * Returns undefined if the blob is malformed or the authentication\n * tag does not match (i.e. wrong key or tampered ciphertext) — the\n * undefined signal lets call sites distinguish \"wrong key\" from\n * \"structurally invalid\" without throwing.\n *\n * - {@link sealEnvelope} and {@link openEnvelope} are convenience helpers\n * that wrap encrypt/decrypt in a structured EncryptedEnvelope shape so\n * the mesh transport layer can handle the binary plumbing uniformly.\n */\n\nimport nacl from \"tweetnacl\";\n\n/** Length in bytes of a secretbox symmetric key. */\nexport const KEY_BYTES = 32;\n/** Length in bytes of a secretbox nonce. */\nexport const NONCE_BYTES = 24;\n/** Length in bytes of the Poly1305 authentication tag. */\nexport const TAG_BYTES = 16;\n\n/**\n * A sealed blob suitable for storage or network transmission. The wire\n * layout is the concatenation of the nonce and the ciphertext+tag from\n * tweetnacl. Callers should not depend on the exact bytes — round-trip\n * through {@link encrypt} / {@link decrypt} or the envelope helpers.\n */\nexport type SealedBytes = Uint8Array;\n\n/** Errors thrown by the encryption subsystem. */\nexport class EncryptionError extends Error {\n readonly code: \"invalid-key-length\" | \"decrypt-failed\" | \"envelope-malformed\";\n constructor(message: string, code: EncryptionError[\"code\"]) {\n super(message);\n this.name = \"EncryptionError\";\n this.code = code;\n }\n}\n\n/**\n * Generate a fresh 32-byte symmetric document key. Calls into tweetnacl's\n * CSPRNG.\n */\nexport function generateDocumentKey(): Uint8Array {\n return nacl.randomBytes(KEY_BYTES);\n}\n\n/**\n * Encrypt a payload under a symmetric key. The returned blob includes a\n * fresh nonce so the receiver does not need any out-of-band coordination\n * to decrypt.\n */\nexport function encrypt(payload: Uint8Array, key: Uint8Array): SealedBytes {\n if (key.length !== KEY_BYTES) {\n throw new EncryptionError(\n `secretbox key must be ${KEY_BYTES} bytes, got ${key.length}.`,\n \"invalid-key-length\"\n );\n }\n const nonce = nacl.randomBytes(NONCE_BYTES);\n const ciphertext = nacl.secretbox(payload, nonce, key);\n const out = new Uint8Array(NONCE_BYTES + ciphertext.length);\n out.set(nonce, 0);\n out.set(ciphertext, NONCE_BYTES);\n return out;\n}\n\n/**\n * Decrypt a sealed blob under a symmetric key. Returns the original\n * payload on success. Returns undefined if the blob is too short to\n * contain a nonce and tag, or if the authentication tag does not match\n * (which indicates either a wrong key or a tampered ciphertext).\n *\n * The undefined return is deliberate: call sites typically want to fall\n * back to a different key or surface a \"could not decrypt\" message rather\n * than catching an exception. Use {@link decryptOrThrow} when an exception\n * is preferred.\n */\nexport function decrypt(sealed: SealedBytes, key: Uint8Array): Uint8Array | undefined {\n if (key.length !== KEY_BYTES) {\n throw new EncryptionError(\n `secretbox key must be ${KEY_BYTES} bytes, got ${key.length}.`,\n \"invalid-key-length\"\n );\n }\n if (sealed.length < NONCE_BYTES + TAG_BYTES) {\n return undefined;\n }\n const nonce = sealed.subarray(0, NONCE_BYTES);\n const ciphertext = sealed.subarray(NONCE_BYTES);\n const opened = nacl.secretbox.open(ciphertext, nonce, key);\n return opened ?? undefined;\n}\n\n/**\n * Decrypt a sealed blob, throwing {@link EncryptionError} on failure\n * instead of returning undefined.\n */\nexport function decryptOrThrow(sealed: SealedBytes, key: Uint8Array): Uint8Array {\n const opened = decrypt(sealed, key);\n if (!opened) {\n throw new EncryptionError(\n `Failed to decrypt sealed blob: wrong key, malformed input, or tampered ciphertext.`,\n \"decrypt-failed\"\n );\n }\n return opened;\n}\n\n/**\n * A high-level structured envelope combining encryption with a small\n * amount of metadata the receiver needs to find the right key. The mesh\n * network adapter uses this shape on the wire.\n */\nexport interface EncryptedEnvelope {\n /** Stable identifier for the document this payload belongs to. The\n * receiver looks this up in its local key store to find the right key. */\n documentId: string;\n /** Sealed blob containing the encrypted payload plus its nonce. */\n sealed: SealedBytes;\n}\n\n/**\n * Encrypt a payload and pack it into an {@link EncryptedEnvelope} along\n * with the document id.\n */\nexport function sealEnvelope(\n payload: Uint8Array,\n documentId: string,\n key: Uint8Array\n): EncryptedEnvelope {\n return {\n documentId,\n sealed: encrypt(payload, key),\n };\n}\n\n/**\n * Decrypt an {@link EncryptedEnvelope} using the given key. Throws on\n * failure for symmetry with {@link sealEnvelope}.\n */\nexport function openEnvelope(envelope: EncryptedEnvelope, key: Uint8Array): Uint8Array {\n return decryptOrThrow(envelope.sealed, key);\n}\n\n/**\n * Serialise an {@link EncryptedEnvelope} to a single binary blob.\n *\n * Wire format:\n *\n * [4 bytes BE: documentId byte length]\n * [N bytes: documentId UTF-8]\n * [remaining: sealed blob (nonce + ciphertext + tag)]\n */\nexport function encodeEncryptedEnvelope(envelope: EncryptedEnvelope): Uint8Array {\n const idBytes = new TextEncoder().encode(envelope.documentId);\n const out = new Uint8Array(4 + idBytes.length + envelope.sealed.length);\n const view = new DataView(out.buffer);\n view.setUint32(0, idBytes.length, false);\n out.set(idBytes, 4);\n out.set(envelope.sealed, 4 + idBytes.length);\n return out;\n}\n\n/**\n * Deserialise a binary envelope produced by {@link encodeEncryptedEnvelope}.\n * Throws on malformed input.\n */\nexport function decodeEncryptedEnvelope(bytes: Uint8Array): EncryptedEnvelope {\n if (bytes.length < 4) {\n throw new EncryptionError(\n `Encrypted envelope too short: ${bytes.length} bytes.`,\n \"envelope-malformed\"\n );\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const idLen = view.getUint32(0, false);\n if (bytes.length < 4 + idLen) {\n throw new EncryptionError(\n `Encrypted envelope truncated: declared id length ${idLen}, total ${bytes.length}.`,\n \"envelope-malformed\"\n );\n }\n const documentId = new TextDecoder().decode(bytes.subarray(4, 4 + idLen));\n const sealed = bytes.slice(4 + idLen);\n return { documentId, sealed };\n}\n",
|
|
6
|
-
"/**\n * mesh-network-adapter — Phase 2 wrapping NetworkAdapter that adds Polly's\n * mesh-transport semantics on top of any underlying Automerge NetworkAdapter.\n *\n * The mesh transport's job is to make every message between peers signed\n * and encrypted before it reaches the wire. Rather than reimplementing the\n * Automerge sync protocol, this adapter takes a base adapter (in production\n * a real WebRTC or WebSocket adapter; in tests an in-memory loopback) and\n * applies the crypto envelope to every message that flows through.\n *\n * Outgoing path (Repo → wire):\n * 1. The Repo's NetworkSubsystem calls send(message) on this adapter.\n * 2. We serialise the message to bytes, encrypt them under the local\n * keyring's document key, sign the resulting blob with the local\n * identity's secret key, and pack the pair into a MeshFrame.\n * 3. We hand the MeshFrame off to the base adapter, which puts it on\n * whatever wire it owns.\n *\n * Incoming path (wire → Repo):\n * 1. The base adapter emits a 'message' event with bytes from the wire.\n * 2. We unpack the MeshFrame, look up the sender's public key in the\n * keyring, verify the signature, look up the document key, decrypt\n * the payload, and deserialise it back to the original message.\n * 3. We re-emit the 'message' event upward to the Repo's NetworkSubsystem\n * with the decrypted message.\n *\n * The keyring is an injected dependency. In production it's backed by\n * persistent storage and populated through the pairing flow. For tests it\n * is just a Map of publicly-known fixtures that both sides share.\n *\n * Caveat for the Phase 2 first cut: Automerge sync messages don't have a\n * stable \"what document does this belong to\" field at the wire level (the\n * documentId is part of the message contents). The mesh adapter therefore\n * uses a single per-Repo encryption key for now rather than per-document\n * keys, and stores the key once in the keyring under the well-known id\n * \"polly-mesh-default\". The plan describes per-document keys as the right\n * end state; that requires either parsing the message to extract the\n * documentId before encrypting (peeking inside the binary protocol) or\n * threading the document context through the network subsystem (which\n * needs upstream support). A follow-up will address this.\n */\n\nimport {\n type Message,\n NetworkAdapter,\n type PeerId,\n type PeerMetadata,\n} from \"@automerge/automerge-repo\";\nimport {\n decodeEncryptedEnvelope,\n encodeEncryptedEnvelope,\n openEnvelope as openEncryptedEnvelope,\n sealEnvelope as sealEncryptedEnvelope,\n} from \"./encryption\";\nimport {\n decodeSignedEnvelope,\n encodeSignedEnvelope,\n openEnvelope as openSignedEnvelope,\n type SigningKeyPair,\n signEnvelope,\n} from \"./signing\";\n\n/** The well-known document id used for the Phase 2 first-cut single-key\n * encryption mode. See the file-level comment for the per-document key\n * follow-up. */\nexport const DEFAULT_MESH_KEY_ID = \"polly-mesh-default\";\n\n/**\n * A mesh keyring holds the local peer's signing identity, the public keys\n * of every peer the local node will accept messages from, the symmetric\n * encryption keys for documents the local node has access to, and the set\n * of peers whose keys have been revoked.\n */\nexport interface MeshKeyring {\n /** The local peer's signing keypair. The secret never leaves this\n * keyring; the public key is gossiped through the access set. */\n identity: SigningKeyPair;\n /** Map from peer id (string) to that peer's signing public key. The\n * mesh adapter rejects messages from peers not present in this map. */\n knownPeers: Map<string, Uint8Array>;\n /** Map from document key id (typically the documentId, or the well-known\n * default for the single-key first cut) to the symmetric encryption key. */\n documentKeys: Map<string, Uint8Array>;\n /** Set of peer ids whose keys have been revoked. The mesh adapter drops\n * incoming messages from any peer in this set, even if the peer is still\n * present in {@link knownPeers}. Revocation is applied via the revocation\n * module; the set is kept separate from knownPeers so that an application\n * can audit who was once authorised without losing the revocation record. */\n revokedPeers: Set<string>;\n /** Optional set of peer ids authorised to issue revocations. When present\n * and non-empty, `decodeRevocation` accepts a signed record only if the\n * issuer is in this set. When undefined or empty, any signed revocation\n * from a known peer is accepted (the Phase 2 first-cut default). This\n * field layers a \"who can revoke whom\" check on top of the signature\n * layer without breaking existing callers. */\n revocationAuthority?: Set<string>;\n}\n\n/**\n * Constructor options for {@link MeshNetworkAdapter}.\n */\nexport interface MeshNetworkAdapterOptions {\n /** The underlying NetworkAdapter that puts crypto-wrapped bytes on the\n * wire. In production this is a WebRTC or WebSocket adapter; in tests\n * it's an in-memory loopback. */\n base: NetworkAdapter;\n /** The local node's keyring. The adapter signs every outgoing message\n * with `identity.secretKey` and verifies every incoming message against\n * the public keys in `knownPeers`. */\n keyring: MeshKeyring;\n /** When false, the adapter signs but does not encrypt. Outgoing messages\n * carry a signature envelope but the payload is plaintext; incoming\n * messages are verified against the sender's public key without a\n * decryption step. This mode is used by $peerState's `sign: true`\n * option, where the server must still be able to parse Automerge sync\n * messages. Defaults to true (encrypt + sign, the full $meshState\n * posture). */\n encryptionEnabled?: boolean;\n}\n\n/**\n * NetworkAdapter that wraps another adapter with Polly's mesh-transport\n * crypto envelope. Every outgoing message is encrypted then signed; every\n * incoming message is verified then decrypted before being forwarded to\n * the Repo's network subsystem.\n *\n * The adapter delegates lifecycle (connect, disconnect, isReady, peer\n * discovery) to the base adapter unchanged. Only the message body is\n * intercepted.\n */\nexport class MeshNetworkAdapter extends NetworkAdapter {\n readonly base: NetworkAdapter;\n readonly keyring: MeshKeyring;\n readonly encryptionEnabled: boolean;\n\n constructor(options: MeshNetworkAdapterOptions) {\n super();\n this.base = options.base;\n this.keyring = options.keyring;\n this.encryptionEnabled = options.encryptionEnabled ?? true;\n\n // Forward lifecycle and peer events from the base adapter.\n this.base.on(\"close\", () => this.emit(\"close\"));\n this.base.on(\"peer-candidate\", (payload) => this.emit(\"peer-candidate\", payload));\n this.base.on(\"peer-disconnected\", (payload) => this.emit(\"peer-disconnected\", payload));\n\n // Intercept incoming messages: the base adapter will surface them as\n // 'message' events with crypto-wrapped payloads. We unwrap and re-emit.\n this.base.on(\"message\", (rawMessage) => {\n const unwrapped = this.tryUnwrap(rawMessage);\n if (unwrapped) {\n this.emit(\"message\", unwrapped);\n }\n // Silently drop messages that fail verification or decryption. A\n // production adapter would surface this through a diagnostic channel\n // so the application could prompt the user; the Phase 2 first cut\n // logs through the standard \"drop unknown\" semantics of the network\n // subsystem.\n });\n }\n\n isReady(): boolean {\n return this.base.isReady();\n }\n\n whenReady(): Promise<void> {\n return this.base.whenReady();\n }\n\n connect(peerId: PeerId, peerMetadata?: PeerMetadata): void {\n this.peerId = peerId;\n if (peerMetadata !== undefined) {\n this.peerMetadata = peerMetadata;\n }\n this.base.connect(peerId, peerMetadata);\n }\n\n disconnect(): void {\n this.base.disconnect();\n }\n\n send(message: Message): void {\n const wrapped = this.wrap(message);\n this.base.send(wrapped);\n }\n\n /**\n * Wrap an outgoing Automerge message in an encrypt-then-sign envelope.\n * The wrapped payload is returned as a Message with the original sender\n * and target ids and the crypto blob in the `data` field.\n */\n private wrap(message: Message): Message {\n const serialised = serialiseMessage(message);\n\n let payloadToSign: Uint8Array;\n if (this.encryptionEnabled) {\n const docKey = this.keyring.documentKeys.get(DEFAULT_MESH_KEY_ID);\n if (!docKey) {\n throw new Error(\n `MeshNetworkAdapter: missing document encryption key under id \"${DEFAULT_MESH_KEY_ID}\". Provision the key in the keyring before sending.`\n );\n }\n const encrypted = sealEncryptedEnvelope(serialised, DEFAULT_MESH_KEY_ID, docKey);\n payloadToSign = encodeEncryptedEnvelope(encrypted);\n } else {\n payloadToSign = serialised;\n }\n\n const signed = signEnvelope(payloadToSign, message.senderId, this.keyring.identity.secretKey);\n const signedBytes = encodeSignedEnvelope(signed);\n\n return {\n type: message.type,\n senderId: message.senderId,\n targetId: message.targetId,\n data: signedBytes,\n } as unknown as Message;\n }\n\n /**\n * Try to unwrap an incoming crypto-wrapped message. Returns the original\n * Message on success, undefined on verification or decryption failure.\n */\n private tryUnwrap(message: Message): Message | undefined {\n if (!message.data) return undefined;\n\n let signed: ReturnType<typeof decodeSignedEnvelope>;\n try {\n signed = decodeSignedEnvelope(message.data);\n } catch {\n return undefined;\n }\n\n // Drop messages from peers whose keys have been revoked, even if the\n // public key is still present in knownPeers. The revocation set is the\n // authoritative \"this peer is no longer trusted\" marker.\n if (this.keyring.revokedPeers.has(signed.senderId)) {\n return undefined;\n }\n\n const senderKey = this.keyring.knownPeers.get(signed.senderId);\n if (!senderKey) {\n return undefined;\n }\n\n let verifiedPayload: Uint8Array;\n try {\n verifiedPayload = openSignedEnvelope(signed, senderKey);\n } catch {\n return undefined;\n }\n\n if (!this.encryptionEnabled) {\n // Sign-only mode: the verified payload IS the serialised message.\n return deserialiseMessage(verifiedPayload);\n }\n\n // Full encrypt+sign mode: unwrap the encryption envelope.\n let encrypted: ReturnType<typeof decodeEncryptedEnvelope>;\n try {\n encrypted = decodeEncryptedEnvelope(verifiedPayload);\n } catch {\n return undefined;\n }\n\n const docKey = this.keyring.documentKeys.get(encrypted.documentId);\n if (!docKey) {\n return undefined;\n }\n\n let plaintext: Uint8Array;\n try {\n plaintext = openEncryptedEnvelope(encrypted, docKey);\n } catch {\n return undefined;\n }\n\n return deserialiseMessage(plaintext);\n }\n}\n\n// ─── message serialisation ─────────────────────────────────────────────────\n\n/**\n * Serialise an Automerge sync message to a binary blob suitable for\n * encryption. The format is a length-prefixed JSON header (carrying the\n * non-binary fields) followed by the raw `data` bytes (which Automerge's\n * sync messages carry as Uint8Array). This avoids round-tripping the\n * binary payload through JSON, which would balloon its size.\n */\nfunction serialiseMessage(message: Message): Uint8Array {\n const headerObj: Record<string, unknown> = {\n type: message.type,\n senderId: message.senderId,\n targetId: message.targetId,\n };\n if (\"documentId\" in message && message.documentId !== undefined) {\n headerObj[\"documentId\"] = message.documentId;\n }\n if (\"count\" in message && message.count !== undefined) {\n headerObj[\"count\"] = message.count;\n }\n if (\"sessionId\" in message && message.sessionId !== undefined) {\n headerObj[\"sessionId\"] = message.sessionId;\n }\n const headerBytes = new TextEncoder().encode(JSON.stringify(headerObj));\n const dataBytes: Uint8Array =\n \"data\" in message && message.data instanceof Uint8Array ? message.data : new Uint8Array(0);\n\n const out = new Uint8Array(4 + headerBytes.length + dataBytes.length);\n const view = new DataView(out.buffer);\n view.setUint32(0, headerBytes.length, false);\n out.set(headerBytes, 4);\n out.set(dataBytes, 4 + headerBytes.length);\n return out;\n}\n\n/**\n * Inverse of {@link serialiseMessage}.\n */\nfunction deserialiseMessage(bytes: Uint8Array): Message {\n if (bytes.length < 4) {\n throw new Error(\"MeshNetworkAdapter: message too short to deserialise.\");\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const headerLen = view.getUint32(0, false);\n if (bytes.length < 4 + headerLen) {\n throw new Error(\"MeshNetworkAdapter: message header truncated.\");\n }\n const header = JSON.parse(new TextDecoder().decode(bytes.subarray(4, 4 + headerLen)));\n const data = bytes.slice(4 + headerLen);\n return { ...header, data } as unknown as Message;\n}\n",
|
|
6
|
+
"/**\n * Eagerly initialise Automerge's WebAssembly runtime by pointing the library\n * at the raw `.wasm` file shipped in `@automerge/automerge`.\n *\n * The `import.meta.url` pattern below is recognised by every modern bundler\n * (Bun, Vite, webpack 5+, esbuild) as an asset reference: the `.wasm` gets\n * copied into the consumer's output alongside the JavaScript, and the URL is\n * rewritten to point at the copied file. `initializeWasm` then fetches and\n * instantiates it at runtime. That keeps the WebAssembly out of the JavaScript\n * bundle entirely — no megabyte-scale base64 string inlined per subpath — and\n * lets the browser parse the JS and stream-compile the `.wasm` in parallel.\n *\n * The top-level `await` makes this module async so any consumer that\n * transitively imports it inherits the \"wait for WASM\" behaviour through the\n * import graph, without needing an explicit init call.\n */\n\nimport wasmPath from \"@automerge/automerge/automerge.wasm\";\nimport { initializeWasm } from \"@automerge/automerge-repo/slim\";\n\n// Bun's file loader returns a relative path; resolving it against the\n// current module's URL yields an absolute URL that fetches correctly\n// regardless of where the consumer's bundler places the script.\nconst wasmUrl = new URL(wasmPath, import.meta.url).href;\n\nawait initializeWasm(wasmUrl);\n",
|
|
7
|
+
"/**\n * blob-cache — local storage backends for blob bytes.\n *\n * Two implementations:\n * - MemoryBlobCache: in-memory Map, suitable for bun:test and server contexts.\n * - IndexedDBBlobCache: persistent IndexedDB store (\"polly-blobs\"), for browsers.\n *\n * Both implement the BlobCache interface from blob-store.ts. Neither uses\n * the existing StorageAdapter — ChromeStorageAdapter JSON-serialises values,\n * which silently destroys Uint8Array data. The blob cache uses IndexedDB\n * directly, which stores typed arrays via structured clone.\n *\n * Both support LRU eviction with pinning. Each `get`/`put` updates the\n * entry's access time; `evict(maxBytes)` drops unpinned entries oldest-first\n * until the total size fits under the target.\n */\n\nimport type { BlobCache } from \"./blob-store\";\n\n// ---------------------------------------------------------------------------\n// MemoryBlobCache\n// ---------------------------------------------------------------------------\n\ninterface MemoryEntry {\n bytes: Uint8Array;\n accessedAt: number;\n}\n\n/** In-memory blob cache backed by a Map. No persistence. Suitable for\n * bun:test (which lacks IndexedDB) and server-side contexts. */\nexport class MemoryBlobCache implements BlobCache {\n private readonly store = new Map<string, MemoryEntry>();\n private readonly pinned = new Set<string>();\n private readonly urls = new Map<string, string>();\n\n async get(hash: string): Promise<Uint8Array | undefined> {\n const entry = this.store.get(hash);\n if (!entry) return undefined;\n entry.accessedAt = Date.now();\n return entry.bytes;\n }\n\n async put(hash: string, bytes: Uint8Array): Promise<void> {\n this.store.set(hash, { bytes, accessedAt: Date.now() });\n }\n\n async has(hash: string): Promise<boolean> {\n return this.store.has(hash);\n }\n\n async delete(hash: string): Promise<void> {\n this.store.delete(hash);\n this.pinned.delete(hash);\n const url = this.urls.get(hash);\n if (url) {\n URL.revokeObjectURL(url);\n this.urls.delete(hash);\n }\n }\n\n async pin(hash: string): Promise<void> {\n this.pinned.add(hash);\n }\n\n async unpin(hash: string): Promise<void> {\n this.pinned.delete(hash);\n }\n\n async size(): Promise<number> {\n let total = 0;\n for (const entry of this.store.values()) {\n total += entry.bytes.byteLength;\n }\n return total;\n }\n\n async evict(maxBytes: number): Promise<number> {\n let currentSize = await this.size();\n if (currentSize <= maxBytes) return 0;\n const freed = currentSize;\n // Sort unpinned entries by accessedAt ascending (oldest first).\n const candidates: Array<{ hash: string; accessedAt: number; size: number }> = [];\n for (const [hash, entry] of this.store) {\n if (!this.pinned.has(hash)) {\n candidates.push({ hash, accessedAt: entry.accessedAt, size: entry.bytes.byteLength });\n }\n }\n candidates.sort((a, b) => a.accessedAt - b.accessedAt);\n\n for (const c of candidates) {\n if (currentSize <= maxBytes) break;\n await this.delete(c.hash);\n currentSize -= c.size;\n }\n return freed - currentSize;\n }\n\n /** Create or return a cached object URL for a blob. Returns undefined\n * if the blob is not in the cache. */\n async url(hash: string): Promise<string | undefined> {\n const cached = this.urls.get(hash);\n if (cached) return cached;\n const entry = this.store.get(hash);\n if (!entry) return undefined;\n const buffer = new ArrayBuffer(entry.bytes.byteLength);\n new Uint8Array(buffer).set(entry.bytes);\n const objectUrl = URL.createObjectURL(new Blob([buffer]));\n this.urls.set(hash, objectUrl);\n return objectUrl;\n }\n\n dispose(): void {\n for (const objectUrl of this.urls.values()) {\n URL.revokeObjectURL(objectUrl);\n }\n this.urls.clear();\n this.store.clear();\n this.pinned.clear();\n }\n}\n\n// ---------------------------------------------------------------------------\n// IndexedDBBlobCache\n// ---------------------------------------------------------------------------\n\ninterface IDBRecord {\n bytes: Uint8Array;\n size: number;\n accessedAt: number;\n pinned: boolean;\n}\n\n/** Persistent blob cache using a dedicated IndexedDB database (\"polly-blobs\").\n * Separate from the \"polly-state\" database used by StorageAdapter. */\nexport class IndexedDBBlobCache implements BlobCache {\n private static readonly DB_NAME = \"polly-blobs\";\n private static readonly DB_VERSION = 1;\n private static readonly STORE_NAME = \"blobs\";\n\n private dbPromise: Promise<IDBDatabase> | null = null;\n private readonly urls = new Map<string, string>();\n\n private openDB(): Promise<IDBDatabase> {\n if (this.dbPromise) return this.dbPromise;\n this.dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(IndexedDBBlobCache.DB_NAME, IndexedDBBlobCache.DB_VERSION);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(IndexedDBBlobCache.STORE_NAME)) {\n db.createObjectStore(IndexedDBBlobCache.STORE_NAME);\n }\n };\n });\n return this.dbPromise;\n }\n\n private async getRecord(hash: string): Promise<IDBRecord | undefined> {\n const db = await this.openDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(IndexedDBBlobCache.STORE_NAME, \"readonly\");\n const store = tx.objectStore(IndexedDBBlobCache.STORE_NAME);\n const request = store.get(hash);\n request.onsuccess = () => resolve(request.result as IDBRecord | undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n private async putRecord(hash: string, record: IDBRecord): Promise<void> {\n const db = await this.openDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(IndexedDBBlobCache.STORE_NAME, \"readwrite\");\n const store = tx.objectStore(IndexedDBBlobCache.STORE_NAME);\n store.put(record, hash);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async get(hash: string): Promise<Uint8Array | undefined> {\n const record = await this.getRecord(hash);\n if (!record) return undefined;\n // Touch accessedAt to implement LRU. Fire-and-forget: the update\n // doesn't need to complete before the caller sees the bytes.\n void this.putRecord(hash, { ...record, accessedAt: Date.now() });\n return record.bytes;\n }\n\n async put(hash: string, bytes: Uint8Array): Promise<void> {\n const existing = await this.getRecord(hash);\n await this.putRecord(hash, {\n bytes,\n size: bytes.byteLength,\n accessedAt: Date.now(),\n pinned: existing?.pinned ?? false,\n });\n }\n\n async has(hash: string): Promise<boolean> {\n const db = await this.openDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(IndexedDBBlobCache.STORE_NAME, \"readonly\");\n const store = tx.objectStore(IndexedDBBlobCache.STORE_NAME);\n const request = store.count(hash);\n request.onsuccess = () => resolve(request.result > 0);\n request.onerror = () => reject(request.error);\n });\n }\n\n async delete(hash: string): Promise<void> {\n const url = this.urls.get(hash);\n if (url) {\n URL.revokeObjectURL(url);\n this.urls.delete(hash);\n }\n const db = await this.openDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(IndexedDBBlobCache.STORE_NAME, \"readwrite\");\n const store = tx.objectStore(IndexedDBBlobCache.STORE_NAME);\n store.delete(hash);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async pin(hash: string): Promise<void> {\n const record = await this.getRecord(hash);\n if (!record) return;\n await this.putRecord(hash, { ...record, pinned: true });\n }\n\n async unpin(hash: string): Promise<void> {\n const record = await this.getRecord(hash);\n if (!record) return;\n await this.putRecord(hash, { ...record, pinned: false });\n }\n\n async size(): Promise<number> {\n const db = await this.openDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(IndexedDBBlobCache.STORE_NAME, \"readonly\");\n const store = tx.objectStore(IndexedDBBlobCache.STORE_NAME);\n const request = store.openCursor();\n let total = 0;\n request.onsuccess = () => {\n const cursor = request.result;\n if (cursor) {\n const value = cursor.value as IDBRecord;\n total += value.size;\n cursor.continue();\n } else {\n resolve(total);\n }\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n async evict(maxBytes: number): Promise<number> {\n const db = await this.openDB();\n const candidates: Array<{ hash: string; accessedAt: number; size: number }> = [];\n let totalSize = 0;\n\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(IndexedDBBlobCache.STORE_NAME, \"readonly\");\n const store = tx.objectStore(IndexedDBBlobCache.STORE_NAME);\n const request = store.openCursor();\n request.onsuccess = () => {\n const cursor = request.result;\n if (cursor) {\n const value = cursor.value as IDBRecord;\n totalSize += value.size;\n if (!value.pinned) {\n candidates.push({\n hash: cursor.key as string,\n accessedAt: value.accessedAt,\n size: value.size,\n });\n }\n cursor.continue();\n } else {\n resolve();\n }\n };\n request.onerror = () => reject(request.error);\n });\n\n if (totalSize <= maxBytes) return 0;\n candidates.sort((a, b) => a.accessedAt - b.accessedAt);\n\n let freed = 0;\n for (const c of candidates) {\n if (totalSize <= maxBytes) break;\n await this.delete(c.hash);\n totalSize -= c.size;\n freed += c.size;\n }\n return freed;\n }\n\n async url(hash: string): Promise<string | undefined> {\n const cached = this.urls.get(hash);\n if (cached) return cached;\n const bytes = await this.get(hash);\n if (!bytes) return undefined;\n const buffer = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(buffer).set(bytes);\n const objectUrl = URL.createObjectURL(new Blob([buffer]));\n this.urls.set(hash, objectUrl);\n return objectUrl;\n }\n\n dispose(): void {\n for (const objectUrl of this.urls.values()) {\n URL.revokeObjectURL(objectUrl);\n }\n this.urls.clear();\n }\n}\n",
|
|
8
|
+
"/**\n * BlobRef — content-addressed reference to a binary blob.\n *\n * CRDT documents held by `$peerState` and `$meshState` primitives should not embed\n * large binary payloads directly. The Automerge history grows monotonically with every\n * op, and embedding a file means every op carries the file, memory explodes, and sync\n * bandwidth is terrible. The sound pattern is to store a reference to the blob inside\n * the document and keep the bytes in a separate, content-addressed blob store.\n *\n * This module defines the reference shape so that applications can hold `BlobRef`\n * values inside their `$peerState`/`$meshState` documents from day one. The actual\n * blob store, the upload/fetch API, and transport-specific blob sync are deliberately\n * out of scope and will land in a follow-up RFC. Provisioning the type now prevents\n * a data-model migration when that RFC ships.\n *\n * @example\n * ```ts\n * const doc = $peerState<Document>(\"doc\", { title: \"\", attachments: [] });\n *\n * const bytes = new Uint8Array(await file.arrayBuffer());\n * const ref = await createBlobRef({\n * bytes,\n * filename: file.name,\n * mimeType: file.type,\n * });\n *\n * doc.value = {\n * ...doc.value,\n * attachments: [...doc.value.attachments, ref],\n * };\n * ```\n */\n\n/**\n * Content-addressed reference to a binary blob.\n *\n * The `hash` field is a hex-encoded SHA-256 digest of the blob's bytes. Two blobs\n * with identical bytes always produce the same hash, so references can be deduplicated\n * and the blob store (when it ships) can use the hash as its primary key.\n */\nexport type BlobRef = {\n /** Hex-encoded SHA-256 digest of the blob's bytes (64 lowercase hex characters). */\n hash: string;\n /** Length of the blob in bytes. */\n size: number;\n /** Filename as supplied by the originating application. Not used for addressing. */\n filename: string;\n /** MIME type as supplied by the originating application. Not used for addressing. */\n mimeType: string;\n};\n\n/**\n * Type guard for {@link BlobRef}. Useful at CRDT document boundaries where values\n * may have been authored by older clients or by clients running different schemas.\n */\nexport function isBlobRef(value: unknown): value is BlobRef {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as unknown as Record<string, unknown>;\n return (\n typeof v[\"hash\"] === \"string\" &&\n /^[0-9a-f]{64}$/.test(v[\"hash\"]) &&\n typeof v[\"size\"] === \"number\" &&\n Number.isInteger(v[\"size\"]) &&\n v[\"size\"] >= 0 &&\n typeof v[\"filename\"] === \"string\" &&\n typeof v[\"mimeType\"] === \"string\"\n );\n}\n\n/**\n * Compute the SHA-256 hex digest of a byte sequence. Pure and deterministic.\n */\nexport async function computeBlobHash(bytes: Uint8Array): Promise<string> {\n // Copy into a fresh ArrayBuffer-backed view so the buffer type is ArrayBuffer\n // rather than the wider ArrayBufferLike, which crypto.subtle.digest's current\n // type signature rejects for SharedArrayBuffer safety.\n const buffer = new ArrayBuffer(bytes.byteLength);\n const copy = new Uint8Array(buffer);\n copy.set(bytes);\n const digest = await crypto.subtle.digest(\"SHA-256\", buffer);\n const view = new Uint8Array(digest);\n let hex = \"\";\n for (const byte of view) {\n hex += byte.toString(16).padStart(2, \"0\");\n }\n return hex;\n}\n\n/**\n * Arguments for {@link createBlobRef}.\n */\nexport type CreateBlobRefArgs = {\n bytes: Uint8Array;\n filename: string;\n mimeType: string;\n};\n\n/**\n * Build a {@link BlobRef} from raw bytes and metadata. The hash is computed from the\n * bytes; the filename and mimeType are carried from the originating application\n * verbatim and do not affect the content address.\n */\nexport async function createBlobRef({\n bytes,\n filename,\n mimeType,\n}: CreateBlobRefArgs): Promise<BlobRef> {\n const hash = await computeBlobHash(bytes);\n return {\n hash,\n size: bytes.byteLength,\n filename,\n mimeType,\n };\n}\n",
|
|
9
|
+
"/**\n * blob-transfer — chunking, reassembly, and wire format for peer-to-peer\n * blob transfer over WebRTC data channels.\n *\n * Blobs are split into fixed-size chunks (64 KiB) and sent as individual\n * data channel messages. The wire format reuses the same length-prefixed\n * header + binary payload layout that MeshWebRTCAdapter uses for Automerge\n * sync messages. Blob messages are distinguished by a `type` field in the\n * JSON header that starts with \"blob-\".\n *\n * Three message types:\n * - blob-chunk: a single chunk of a blob transfer\n * - blob-request: ask a peer to send chunks for a specific hash\n * - blob-have: announce local availability of a blob\n */\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default chunk size in bytes: 64 KiB. Stays well within WebRTC SCTP\n * message size limits (~256 KiB in most browsers). */\nexport const BLOB_CHUNK_SIZE = 65_536;\n\n/** High-water mark for RTCDataChannel.bufferedAmount. The sender pauses\n * when the buffer exceeds this threshold. */\nexport const BLOB_BUFFER_HIGH_WATER = 256 * 1024;\n\n// ---------------------------------------------------------------------------\n// Message header types\n// ---------------------------------------------------------------------------\n\nexport interface BlobChunkHeader {\n type: \"blob-chunk\";\n hash: string;\n index: number;\n total: number;\n}\n\nexport interface BlobRequestHeader {\n type: \"blob-request\";\n hash: string;\n /** Chunk indices needed. Omit to request all chunks. */\n missing?: number[];\n}\n\nexport interface BlobHaveHeader {\n type: \"blob-have\";\n hash: string;\n}\n\nexport type BlobMessageHeader = BlobChunkHeader | BlobRequestHeader | BlobHaveHeader;\n\n// ---------------------------------------------------------------------------\n// Chunking\n// ---------------------------------------------------------------------------\n\n/** Split bytes into fixed-size chunks. The last chunk may be smaller. */\nexport function chunkBlob(bytes: Uint8Array, chunkSize: number = BLOB_CHUNK_SIZE): Uint8Array[] {\n const chunks: Uint8Array[] = [];\n for (let offset = 0; offset < bytes.length; offset += chunkSize) {\n chunks.push(bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length)));\n }\n // An empty blob produces one empty chunk so the protocol always sends\n // at least one blob-chunk message.\n if (chunks.length === 0) {\n chunks.push(new Uint8Array(0));\n }\n return chunks;\n}\n\n/** Reassemble chunks into a single Uint8Array. Throws if any chunk index\n * in [0, total) is missing from the map. */\nexport function reassembleChunks(chunks: Map<number, Uint8Array>, total: number): Uint8Array {\n let totalBytes = 0;\n for (let i = 0; i < total; i++) {\n const chunk = chunks.get(i);\n if (!chunk) {\n throw new Error(`reassembleChunks: missing chunk ${i} of ${total}`);\n }\n totalBytes += chunk.length;\n }\n const out = new Uint8Array(totalBytes);\n let offset = 0;\n for (let i = 0; i < total; i++) {\n const chunk = chunks.get(i) as Uint8Array;\n out.set(chunk, offset);\n offset += chunk.length;\n }\n return out;\n}\n\n/** Return the indices of chunks missing from a partial chunk map. */\nexport function missingChunkIndices(chunks: Map<number, Uint8Array>, total: number): number[] {\n const missing: number[] = [];\n for (let i = 0; i < total; i++) {\n if (!chunks.has(i)) {\n missing.push(i);\n }\n }\n return missing;\n}\n\n// ---------------------------------------------------------------------------\n// Wire format\n// ---------------------------------------------------------------------------\n\n/** Serialise a blob message into the shared wire format:\n * [4-byte BE header length][JSON header bytes][binary payload].\n * Returns an ArrayBuffer-backed Uint8Array usable by RTCDataChannel.send. */\nexport function serialiseBlobMessage(\n header: BlobMessageHeader,\n data: Uint8Array = new Uint8Array(0)\n): Uint8Array<ArrayBuffer> {\n const headerBytes = new TextEncoder().encode(JSON.stringify(header));\n const size = 4 + headerBytes.length + data.length;\n const buffer = new ArrayBuffer(size);\n const out = new Uint8Array(buffer);\n const view = new DataView(buffer);\n view.setUint32(0, headerBytes.length, false);\n out.set(headerBytes, 4);\n out.set(data, 4 + headerBytes.length);\n return out;\n}\n\n/** Peek at the header of a wire-format message without full\n * deserialisation. Returns the parsed header and the data slice,\n * or undefined if the bytes are too short or malformed. */\nexport function parseBlobMessage(\n bytes: Uint8Array\n): { header: BlobMessageHeader; data: Uint8Array } | undefined {\n if (bytes.length < 4) return undefined;\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const headerLen = view.getUint32(0, false);\n if (bytes.length < 4 + headerLen) return undefined;\n try {\n const header = JSON.parse(\n new TextDecoder().decode(bytes.subarray(4, 4 + headerLen))\n ) as BlobMessageHeader;\n const data = bytes.subarray(4 + headerLen);\n return { header, data };\n } catch {\n return undefined;\n }\n}\n\n/** Check whether a wire-format message header type starts with \"blob-\". */\nexport function isBlobMessageType(bytes: Uint8Array): boolean {\n if (bytes.length < 4) return false;\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const headerLen = view.getUint32(0, false);\n if (bytes.length < 4 + headerLen) return false;\n // Fast check: look for \"blob-\" in the header JSON without full parse.\n // The type field is always near the start of the JSON object.\n const headerSlice = bytes.subarray(4, 4 + headerLen);\n // Search for the byte sequence: \"type\":\"blob-\n const needle = new TextEncoder().encode('\"type\":\"blob-');\n return findSubarray(headerSlice, needle) !== -1;\n}\n\n/** Find the first occurrence of needle in haystack. Returns -1 if not found. */\nfunction findSubarray(haystack: Uint8Array, needle: Uint8Array): number {\n if (needle.length === 0) return 0;\n outer: for (let i = 0; i <= haystack.length - needle.length; i++) {\n for (let j = 0; j < needle.length; j++) {\n if (haystack[i + j] !== needle[j]) continue outer;\n }\n return i;\n }\n return -1;\n}\n",
|
|
10
|
+
"/**\n * blob-store-impl — peer-to-peer blob store backed by WebRTC data channels.\n *\n * The store piggybacks on an existing MeshWebRTCAdapter. Blob messages ride\n * on the same data channel as Automerge sync traffic, distinguished by a\n * \"blob-\" prefix on the message type field.\n *\n * Lifecycle:\n * - put: hash-verify → cache plaintext locally → announce blob-have\n * - get: check cache → if miss, send blob-request(s) → receive chunks\n * → decrypt each chunk as it arrives → reassemble plaintext\n * → hash-verify → cache\n *\n * Encryption model: chunk-then-encrypt. The sender chunks the plaintext\n * into 64 KiB pieces, encrypts each chunk independently under the configured\n * key (with a fresh random nonce per chunk), and sends the sealed envelope.\n * The receiver decrypts each chunk on arrival. Peak sender memory is ~1\n * chunk of ciphertext at a time rather than the entire ciphertext blob.\n *\n * Per-op keys: callers can override the store's default encryption key per\n * put/get via BlobTransferOptions.key. This supports per-document keys\n * without requiring the store to track document ownership.\n *\n * Multi-source fetch: the get() flow requests from a single peer initially,\n * but the re-request timer rotates through peers that have announced\n * availability of the blob, so a transfer that stalls on one peer recovers\n * against another.\n */\n\nimport { MemoryBlobCache } from \"./blob-cache\";\nimport { computeBlobHash } from \"./blob-ref\";\nimport type { BlobCache, BlobProgressCallback, BlobStore, BlobStoreOptions } from \"./blob-store\";\nimport {\n type BlobChunkHeader,\n type BlobHaveHeader,\n type BlobMessageHeader,\n type BlobRequestHeader,\n chunkBlob,\n missingChunkIndices,\n reassembleChunks,\n serialiseBlobMessage,\n} from \"./blob-transfer\";\nimport type { MeshWebRTCAdapter } from \"./mesh-webrtc-adapter\";\n\nconst DEFAULT_MAX_BLOB_SIZE = 100 * 1024 * 1024; // 100 MiB\nconst DOWNLOAD_TIMEOUT_MS = 60_000;\nconst RE_REQUEST_DELAY_MS = 5_000;\n\n/** State for an in-progress blob download. */\ninterface DownloadState {\n total: number;\n /** Decrypted plaintext chunks, keyed by index. */\n chunks: Map<number, Uint8Array>;\n resolve: (bytes: Uint8Array) => void;\n reject: (error: Error) => void;\n onProgress?: BlobProgressCallback;\n timeoutId?: ReturnType<typeof setTimeout>;\n reRequestId?: ReturnType<typeof setTimeout>;\n /** Encryption key for this download, if any. */\n key?: Uint8Array;\n /** Peers we've asked, rotated on re-request for multi-source fetch. */\n peersAttempted: string[];\n /** Index into peersAttempted for the next re-request. */\n peerRotationIndex: number;\n}\n\n/** Create a blob store that transfers blobs peer-to-peer over a\n * MeshWebRTCAdapter's data channels. */\nexport function createBlobStore(adapter: MeshWebRTCAdapter, options?: BlobStoreOptions): BlobStore {\n const maxBlobSize = options?.maxBlobSize ?? DEFAULT_MAX_BLOB_SIZE;\n const defaultKey = options?.encrypt?.key;\n const cache: BlobCache = options?.cache ?? new MemoryBlobCache();\n\n // Per-hash key used at put time. Retained so that blob-request handlers\n // can encrypt chunks with the same key they were put under.\n const keysByHash = new Map<string, Uint8Array>();\n // Track which peers have which blobs (hash → set of peer IDs).\n const peerBlobs = new Map<string, Set<string>>();\n // Track in-progress downloads (hash → download state).\n const downloads = new Map<string, DownloadState>();\n // Hashes we have locally (for announcing to new peers).\n const localHashes = new Set<string>();\n // Object URL cache (hash → object URL string).\n const urlCache = new Map<string, string>();\n let disposed = false;\n\n // Wire up the adapter's blob message callback.\n adapter.onBlobMessage = (peerId, header, data) => {\n if (disposed) return;\n const type = header[\"type\"] as string;\n switch (type) {\n case \"blob-chunk\":\n void handleChunk(peerId, header as unknown as BlobChunkHeader, data);\n break;\n case \"blob-request\":\n void handleRequest(peerId, header as unknown as BlobRequestHeader);\n break;\n case \"blob-have\":\n handleHave(peerId, header as unknown as BlobHaveHeader);\n break;\n }\n };\n\n // Announce local blobs to new peers when they connect.\n const peerCandidateHandler = (event: { peerId: unknown }) => {\n if (disposed) return;\n const newPeerId = event.peerId as unknown as string;\n for (const hash of localHashes) {\n const msg = serialiseBlobMessage({ type: \"blob-have\", hash } as BlobMessageHeader);\n adapter.sendBlobMessage(newPeerId, msg);\n }\n };\n adapter.on(\"peer-candidate\", peerCandidateHandler);\n\n // -----------------------------------------------------------------------\n // Crypto helpers\n // -----------------------------------------------------------------------\n\n async function encryptChunk(plaintext: Uint8Array, key: Uint8Array): Promise<Uint8Array> {\n const { encrypt } = await import(\"./encryption\");\n return encrypt(plaintext, key);\n }\n\n async function decryptChunk(\n sealed: Uint8Array,\n key: Uint8Array\n ): Promise<Uint8Array | undefined> {\n const { decrypt } = await import(\"./encryption\");\n return decrypt(sealed, key);\n }\n\n // -----------------------------------------------------------------------\n // Incoming message handlers\n // -----------------------------------------------------------------------\n\n async function handleChunk(\n peerId: string,\n header: BlobChunkHeader,\n data: Uint8Array\n ): Promise<void> {\n const download = downloads.get(header.hash);\n if (!download) return;\n download.total = header.total;\n\n // Track this peer as a source of chunks for this blob.\n if (!download.peersAttempted.includes(peerId)) {\n download.peersAttempted.push(peerId);\n }\n\n // Decrypt if a key is configured.\n let chunkBytes: Uint8Array;\n if (download.key) {\n const plaintext = await decryptChunk(data, download.key);\n if (!plaintext) {\n // Skip this chunk — bad key or corrupted. Let re-request retry.\n return;\n }\n chunkBytes = plaintext;\n } else {\n chunkBytes = data.slice();\n }\n\n download.chunks.set(header.index, chunkBytes);\n\n reportChunkProgress(download);\n resetReRequestTimer(download);\n\n if (download.chunks.size >= header.total) {\n finishDownload(header.hash, download);\n } else {\n scheduleReRequest(header.hash, download);\n }\n }\n\n function reportChunkProgress(download: DownloadState): void {\n if (!download.onProgress || download.total <= 0) return;\n let loaded = 0;\n for (const chunk of download.chunks.values()) {\n loaded += chunk.length;\n }\n download.onProgress({ loaded, total: undefined, phase: \"downloading\" });\n }\n\n function resetReRequestTimer(download: DownloadState): void {\n if (download.reRequestId) {\n clearTimeout(download.reRequestId);\n download.reRequestId = undefined;\n }\n }\n\n function finishDownload(hash: string, download: DownloadState): void {\n clearDownloadTimers(download);\n try {\n const assembled = reassembleChunks(download.chunks, download.total);\n downloads.delete(hash);\n download.resolve(assembled);\n } catch (err) {\n downloads.delete(hash);\n download.reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n async function handleRequest(peerId: string, header: BlobRequestHeader): Promise<void> {\n const plaintext = await cache.get(header.hash);\n if (!plaintext) return;\n\n const plaintextChunks = chunkBlob(plaintext);\n const requested = header.missing ?? plaintextChunks.map((_, i) => i);\n const chunkKey = keysByHash.get(header.hash);\n\n for (const index of requested) {\n await sendChunkAtIndex(peerId, header.hash, plaintextChunks, index, chunkKey);\n }\n }\n\n async function sendChunkAtIndex(\n peerId: string,\n hash: string,\n plaintextChunks: Uint8Array[],\n index: number,\n chunkKey: Uint8Array | undefined\n ): Promise<void> {\n if (index < 0 || index >= plaintextChunks.length) return;\n const plainChunk = plaintextChunks[index];\n if (!plainChunk) return;\n\n const payload = chunkKey ? await encryptChunk(plainChunk, chunkKey) : plainChunk;\n const chunkHeader: BlobChunkHeader = {\n type: \"blob-chunk\",\n hash,\n index,\n total: plaintextChunks.length,\n };\n const msg = serialiseBlobMessage(chunkHeader as BlobMessageHeader, payload);\n\n if (!adapter.sendBlobMessage(peerId, msg)) {\n await waitForBufferDrain();\n adapter.sendBlobMessage(peerId, msg);\n }\n }\n\n function handleHave(peerId: string, header: BlobHaveHeader): void {\n let peers = peerBlobs.get(header.hash);\n if (!peers) {\n peers = new Set();\n peerBlobs.set(header.hash, peers);\n }\n peers.add(peerId);\n }\n\n // -----------------------------------------------------------------------\n // Helpers\n // -----------------------------------------------------------------------\n\n function announceHave(hash: string): void {\n const msg = serialiseBlobMessage({ type: \"blob-have\", hash } as BlobMessageHeader);\n for (const peerId of adapter.connectedPeerIds) {\n adapter.sendBlobMessage(peerId, msg);\n }\n }\n\n function clearDownloadTimers(download: DownloadState): void {\n if (download.timeoutId) clearTimeout(download.timeoutId);\n if (download.reRequestId) clearTimeout(download.reRequestId);\n }\n\n function scheduleReRequest(hash: string, download: DownloadState): void {\n download.reRequestId = setTimeout(() => fireReRequest(hash, download), RE_REQUEST_DELAY_MS);\n }\n\n function fireReRequest(hash: string, download: DownloadState): void {\n if (!downloads.has(hash)) return;\n const missing = missingChunkIndices(download.chunks, download.total);\n if (missing.length === 0) return;\n\n // Multi-source fetch: rotate through known peers so a stalled\n // transfer recovers against a different source.\n const peers = peerBlobs.get(hash);\n const pool = peers && peers.size > 0 ? Array.from(peers) : Array.from(adapter.connectedPeerIds);\n if (pool.length === 0) return;\n const target = pool[download.peerRotationIndex % pool.length];\n download.peerRotationIndex++;\n if (!target) return;\n\n const reqHeader: BlobRequestHeader = { type: \"blob-request\", hash, missing };\n const msg = serialiseBlobMessage(reqHeader as BlobMessageHeader);\n adapter.sendBlobMessage(target, msg);\n\n // Re-arm timer in case this peer also stalls.\n scheduleReRequest(hash, download);\n }\n\n function waitForBufferDrain(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n // -----------------------------------------------------------------------\n // BlobStore implementation\n // -----------------------------------------------------------------------\n\n const store: BlobStore = {\n async put(ref, bytes, options?): Promise<void> {\n if (disposed) throw new Error(\"BlobStore is disposed\");\n options?.signal?.throwIfAborted();\n\n if (bytes.length > maxBlobSize) {\n throw new Error(`Blob exceeds maximum size (${bytes.length} > ${maxBlobSize})`);\n }\n\n // Verify hash.\n const hash = await computeBlobHash(bytes);\n if (hash !== ref.hash) {\n throw new Error(`Hash mismatch: expected ${ref.hash}, got ${hash}`);\n }\n\n options?.signal?.throwIfAborted();\n\n // Resolve the encryption key: per-op override > store default > no\n // encryption. Record the key so incoming blob-requests encrypt with\n // the same one.\n const key = options?.key ?? defaultKey;\n if (key) {\n keysByHash.set(ref.hash, key);\n }\n\n options?.onProgress?.({ loaded: bytes.length, total: bytes.length, phase: \"uploading\" });\n\n // Store plaintext in cache. Chunking + encryption is deferred to\n // handleRequest for streaming behavior.\n await cache.put(ref.hash, bytes);\n localHashes.add(ref.hash);\n\n announceHave(ref.hash);\n },\n\n async get(hash, options?): Promise<Uint8Array | undefined> {\n if (disposed) throw new Error(\"BlobStore is disposed\");\n options?.signal?.throwIfAborted();\n\n // Check local cache first.\n const cached = await cache.get(hash);\n if (cached) return cached;\n\n // Resolve the decryption key: per-op override > store default > no\n // decryption (plaintext chunks).\n const key = options?.key ?? defaultKey;\n\n // Find peers that have this blob.\n const peers = peerBlobs.get(hash);\n const candidates =\n peers && peers.size > 0 ? Array.from(peers) : Array.from(adapter.connectedPeerIds);\n const targetPeer = candidates[0];\n if (!targetPeer) return undefined;\n\n const requestHeader: BlobRequestHeader = { type: \"blob-request\", hash };\n const msg = serialiseBlobMessage(requestHeader as BlobMessageHeader);\n adapter.sendBlobMessage(targetPeer, msg);\n\n // Wait for chunks to arrive.\n const plaintext = await new Promise<Uint8Array>((resolve, reject) => {\n const download: DownloadState = {\n total: 0,\n chunks: new Map(),\n resolve,\n reject,\n onProgress: options?.onProgress,\n key,\n peersAttempted: [targetPeer],\n peerRotationIndex: 1,\n };\n downloads.set(hash, download);\n\n options?.signal?.addEventListener(\n \"abort\",\n () => {\n if (downloads.has(hash)) {\n clearDownloadTimers(download);\n downloads.delete(hash);\n reject(new Error(\"Blob download aborted\"));\n }\n },\n { once: true }\n );\n\n download.timeoutId = setTimeout(() => {\n if (downloads.has(hash)) {\n clearDownloadTimers(download);\n downloads.delete(hash);\n reject(new Error(\"Blob download timed out\"));\n }\n }, DOWNLOAD_TIMEOUT_MS);\n });\n\n // Verify hash against the plaintext.\n const actualHash = await computeBlobHash(plaintext);\n if (actualHash !== hash) {\n throw new Error(`Blob hash mismatch after download: expected ${hash}, got ${actualHash}`);\n }\n\n // Cache locally. Record the key so future blob-requests from peers\n // use the same one.\n await cache.put(hash, plaintext);\n if (key) keysByHash.set(hash, key);\n localHashes.add(hash);\n\n return plaintext;\n },\n\n async url(hash): Promise<string | undefined> {\n if (disposed) return undefined;\n const cached = urlCache.get(hash);\n if (cached) return cached;\n const bytes = await cache.get(hash);\n if (!bytes) return undefined;\n const buffer = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(buffer).set(bytes);\n const objectUrl = URL.createObjectURL(new Blob([buffer]));\n urlCache.set(hash, objectUrl);\n return objectUrl;\n },\n\n async pin(hash): Promise<void> {\n await cache.pin(hash);\n },\n\n async unpin(hash): Promise<void> {\n await cache.unpin(hash);\n },\n\n async size(): Promise<number> {\n return cache.size();\n },\n\n async evict(maxBytes): Promise<number> {\n return cache.evict(maxBytes);\n },\n\n dispose(): void {\n disposed = true;\n adapter.onBlobMessage = undefined;\n adapter.off(\"peer-candidate\", peerCandidateHandler);\n for (const [hash, download] of downloads) {\n clearDownloadTimers(download);\n download.reject(new Error(\"BlobStore disposed\"));\n downloads.delete(hash);\n }\n for (const objectUrl of urlCache.values()) {\n URL.revokeObjectURL(objectUrl);\n }\n urlCache.clear();\n keysByHash.clear();\n cache.dispose();\n },\n };\n\n return store;\n}\n",
|
|
11
|
+
"/**\n * @fairfox/polly/mesh — mesh-networked CRDT state with end-to-end encryption.\n *\n * Wraps the peer-first primitives with WebRTC mesh transport and\n * sign-then-encrypt envelopes. This subpath export isolates the\n * @automerge/* and tweetnacl dependency trees so consumers who only\n * use $sharedState / $syncedState / $resource never pull them in.\n *\n * @example\n * ```typescript\n * import { $meshState, configureMeshState } from \"@fairfox/polly/mesh\";\n * ```\n */\n\n// Eagerly initialise Automerge's WASM runtime. Must come before anything\n// that touches @automerge/automerge-repo/slim.\nimport \"./shared/lib/wasm-init\";\n\nexport { IndexedDBBlobCache, MemoryBlobCache } from \"./shared/lib/blob-cache\";\nexport type { BlobRef, CreateBlobRefArgs } from \"./shared/lib/blob-ref\";\nexport { computeBlobHash, createBlobRef, isBlobRef } from \"./shared/lib/blob-ref\";\n// Blob store\nexport type {\n BlobCache,\n BlobProgressCallback,\n BlobProgressEvent,\n BlobStore,\n BlobStoreOptions,\n BlobTransferOptions,\n} from \"./shared/lib/blob-store\";\nexport { createBlobStore } from \"./shared/lib/blob-store-impl\";\nexport type {\n EncryptedEnvelope,\n SealedBytes,\n} from \"./shared/lib/encryption\";\nexport {\n decrypt,\n decryptOrThrow,\n EncryptionError,\n encrypt,\n generateDocumentKey,\n KEY_BYTES as ENCRYPTION_KEY_BYTES,\n NONCE_BYTES as ENCRYPTION_NONCE_BYTES,\n TAG_BYTES as ENCRYPTION_TAG_BYTES,\n} from \"./shared/lib/encryption\";\n// Keyring storage (runtime-agnostic)\nexport type { KeyringStorage } from \"./shared/lib/keyring-storage\";\nexport {\n deserialiseKeyring,\n memoryKeyringStorage,\n serialiseKeyring,\n} from \"./shared/lib/keyring-storage\";\n// Mesh client factory\nexport type { CreateMeshClientOptions, MeshClient } from \"./shared/lib/mesh-client\";\nexport { createMeshClient } from \"./shared/lib/mesh-client\";\n// Mesh network adapter (sign-then-encrypt envelopes over any base adapter)\nexport type {\n MeshKeyring,\n MeshNetworkAdapterOptions,\n} from \"./shared/lib/mesh-network-adapter\";\nexport {\n DEFAULT_MESH_KEY_ID,\n MeshNetworkAdapter,\n} from \"./shared/lib/mesh-network-adapter\";\n// Mesh signaling client\nexport type {\n MeshSignalingClientOptions,\n SignalingMessage as MeshSignalingMessage,\n} from \"./shared/lib/mesh-signaling-client\";\nexport { MeshSignalingClient } from \"./shared/lib/mesh-signaling-client\";\n// Mesh state wrappers\nexport type { MeshStateOptions } from \"./shared/lib/mesh-state\";\nexport {\n $meshCounter,\n $meshList,\n $meshState,\n $meshText,\n configureMeshState,\n resetMeshState,\n} from \"./shared/lib/mesh-state\";\n// Mesh WebRTC adapter\nexport type { MeshWebRTCAdapterOptions } from \"./shared/lib/mesh-webrtc-adapter\";\nexport { DEFAULT_ICE_SERVERS, MeshWebRTCAdapter } from \"./shared/lib/mesh-webrtc-adapter\";\n// Pairing and revocation (depend on signing/encryption)\nexport type {\n CreatePairingTokenOptions,\n PairingToken,\n} from \"./shared/lib/pairing\";\nexport {\n applyPairingToken,\n createPairingToken,\n createPairingTokenWithFreshIdentity,\n DEFAULT_PAIRING_TTL_MS,\n decodePairingToken,\n encodePairingToken,\n isPairingTokenExpired,\n PAIRING_NONCE_BYTES,\n PAIRING_TOKEN_VERSION,\n PairingError,\n parsePairingToken,\n serialisePairingToken,\n} from \"./shared/lib/pairing\";\nexport type {\n CreateRevocationOptions,\n RevocationRecord,\n} from \"./shared/lib/revocation\";\nexport {\n applyRevocation,\n createRevocation,\n decodeRevocation,\n encodeRevocation,\n REVOCATION_MAGIC,\n REVOCATION_RECORD_VERSION,\n RevocationError,\n revokePeerLocally,\n} from \"./shared/lib/revocation\";\n// Cryptographic primitives (signing + encryption)\nexport type { SignedEnvelope, SigningKeyPair } from \"./shared/lib/signing\";\nexport {\n generateSigningKeyPair,\n PUBLIC_KEY_BYTES as SIGNING_PUBLIC_KEY_BYTES,\n SECRET_KEY_BYTES as SIGNING_SECRET_KEY_BYTES,\n SIGNATURE_BYTES as SIGNING_SIGNATURE_BYTES,\n SigningError,\n sign,\n signingKeyPairFromSecret,\n verify,\n} from \"./shared/lib/signing\";\n",
|
|
12
|
+
"/**\n * keyring-storage — persistence abstraction for {@link MeshKeyring}.\n *\n * The keyring itself is a plain structural object of `Map`s, `Set`s, and a\n * signing keypair; it is deliberately not coupled to any persistence layer.\n * This module defines a storage interface that applications implement once\n * for their runtime (IndexedDB, the filesystem, a keychain, a secret\n * manager, whatever) and wire into {@link createMeshClient} via its\n * `keyring.storage` option.\n *\n * A canonical JSON-with-base64 serialisation is provided by\n * {@link serialiseKeyring} and {@link deserialiseKeyring}. It is inspectable\n * by humans, survives manual edits, and round-trips every field of the\n * keyring. Storage implementations that write plain strings (files,\n * localStorage, `kv` stores) can lean on these helpers; storage\n * implementations that persist structured data (IndexedDB, a keychain API)\n * can serialise differently if they prefer.\n */\n\nimport type { MeshKeyring } from \"./mesh-network-adapter\";\nimport type { SigningKeyPair } from \"./signing\";\n\n/**\n * A load/save pair for a single {@link MeshKeyring}. Implementations are\n * free to choose where and how the keyring is stored; the factory only\n * cares that `load()` returns the previously-saved keyring or `null`, and\n * that `save(keyring)` durably persists it.\n */\nexport interface KeyringStorage {\n /** Load the previously-saved keyring, or return `null` if none exists.\n * Implementations may throw for truly exceptional conditions (disk\n * errors, permission failures); a missing keyring is not exceptional. */\n load(): Promise<MeshKeyring | null>;\n /** Durably persist the keyring. Implementations should atomically replace\n * any existing stored value; partial writes must not leave the store in\n * an inconsistent state. */\n save(keyring: MeshKeyring): Promise<void>;\n}\n\n/**\n * In-memory storage. Useful for tests, ephemeral tools, and the first-run\n * bootstrap path where the keyring only lives for the duration of the\n * process. Calling `save` holds the keyring in a closed-over variable;\n * `load` returns it on subsequent calls within the same process.\n */\nexport function memoryKeyringStorage(): KeyringStorage {\n let stored: MeshKeyring | null = null;\n return {\n load: async () => stored,\n save: async (keyring) => {\n stored = keyring;\n },\n };\n}\n\n// ─── Canonical JSON+base64 serialisation ───────────────────────────────────\n\ninterface SerialisedKeyring {\n version: 1;\n identity: { publicKey: string; secretKey: string };\n knownPeers: Record<string, string>;\n documentKeys: Record<string, string>;\n revokedPeers: string[];\n revocationAuthority?: string[];\n}\n\n/**\n * Encode a {@link MeshKeyring} to a canonical JSON string. Every\n * `Uint8Array` field (identity keys, public keys, document keys) is\n * base64-encoded; `Map`s and `Set`s become plain objects and arrays. The\n * output is pretty-printed so a human operator can eyeball or hand-edit\n * the file on disk.\n */\nexport function serialiseKeyring(keyring: MeshKeyring): string {\n const payload: SerialisedKeyring = {\n version: 1,\n identity: {\n publicKey: bytesToBase64(keyring.identity.publicKey),\n secretKey: bytesToBase64(keyring.identity.secretKey),\n },\n knownPeers: mapToBase64Record(keyring.knownPeers),\n documentKeys: mapToBase64Record(keyring.documentKeys),\n revokedPeers: [...keyring.revokedPeers],\n };\n if (keyring.revocationAuthority && keyring.revocationAuthority.size > 0) {\n payload.revocationAuthority = [...keyring.revocationAuthority];\n }\n return JSON.stringify(payload, null, 2);\n}\n\n/**\n * Decode a keyring from the format produced by {@link serialiseKeyring}.\n * Throws with a descriptive message when the input is malformed, so\n * corrupt storage surfaces as an actionable error rather than a silent\n * downgrade.\n */\nexport function deserialiseKeyring(text: string): MeshKeyring {\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch (err) {\n throw new Error(`KeyringStorage: keyring payload is not valid JSON: ${(err as Error).message}`);\n }\n if (!raw || typeof raw !== \"object\") {\n throw new Error(\"KeyringStorage: keyring payload is not an object\");\n }\n const r = raw as Partial<SerialisedKeyring>;\n if (r.version !== 1) {\n throw new Error(`KeyringStorage: unsupported keyring version: ${String(r.version)}`);\n }\n if (!r.identity || typeof r.identity !== \"object\") {\n throw new Error(\"KeyringStorage: keyring payload is missing identity\");\n }\n const identity: SigningKeyPair = {\n publicKey: base64ToBytes(r.identity.publicKey),\n secretKey: base64ToBytes(r.identity.secretKey),\n };\n const keyring: MeshKeyring = {\n identity,\n knownPeers: base64RecordToMap(r.knownPeers ?? {}),\n documentKeys: base64RecordToMap(r.documentKeys ?? {}),\n revokedPeers: new Set(r.revokedPeers ?? []),\n };\n if (r.revocationAuthority && r.revocationAuthority.length > 0) {\n keyring.revocationAuthority = new Set(r.revocationAuthority);\n }\n return keyring;\n}\n\nfunction mapToBase64Record(map: Map<string, Uint8Array>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of map) {\n out[key] = bytesToBase64(value);\n }\n return out;\n}\n\nfunction base64RecordToMap(record: Record<string, string>): Map<string, Uint8Array> {\n const out = new Map<string, Uint8Array>();\n for (const [key, value] of Object.entries(record)) {\n out.set(key, base64ToBytes(value));\n }\n return out;\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // btoa is available in browsers, Node 16+, and Bun.\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary);\n}\n\nfunction base64ToBytes(b64: string): Uint8Array {\n const binary = atob(b64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n",
|
|
13
|
+
"/**\n * mesh-client — first-class factory for constructing a Polly mesh client.\n *\n * The mesh transport stack has five pieces that have to be wired together:\n * a {@link MeshSignalingClient} talking to the relay server, a\n * {@link MeshWebRTCAdapter} that owns the per-peer RTCPeerConnections, a\n * {@link MeshNetworkAdapter} that signs and encrypts every message on the\n * way through, an Automerge `Repo` that drives sync, and a `MeshKeyring`\n * that holds the crypto material. Prior to this module, every consuming\n * application had to assemble the five pieces by hand — and in Node or\n * Bun had to monkey-patch `globalThis.WebSocket` / `globalThis.RTCPeerConnection`\n * because the lower-level primitives reached for those globals.\n *\n * `createMeshClient` takes options, hands back a `MeshClient`, and also\n * calls `configureMeshState(client.repo)` so `$meshState` works without\n * a second setup step. The WebSocket and RTCPeerConnection implementations\n * are injectable; defaults read from `globalThis` for browser ergonomics.\n * The companion `@fairfox/polly/mesh/node` subpath provides a CLI bootstrap\n * helper that wires werift (or `@roamhq/wrtc` if the consumer prefers) and\n * a file-backed keyring store.\n */\n\nimport { Repo, type StorageAdapterInterface } from \"@automerge/automerge-repo/slim\";\nimport type { KeyringStorage } from \"./keyring-storage\";\nimport { DEFAULT_MESH_KEY_ID, type MeshKeyring, MeshNetworkAdapter } from \"./mesh-network-adapter\";\nimport { MeshSignalingClient, type MeshSignalingClientOptions } from \"./mesh-signaling-client\";\nimport { configureMeshState } from \"./mesh-state\";\nimport { MeshWebRTCAdapter, type MeshWebRTCAdapterOptions } from \"./mesh-webrtc-adapter\";\n\n/** Options for {@link createMeshClient}. */\nexport interface CreateMeshClientOptions {\n /** Signalling-server configuration. `peerId` must be the same identity\n * this client's keyring was paired with on other peers. */\n signaling: {\n url: string;\n peerId: string;\n /** Optional WebSocket ctor override (e.g. `ws` on old Node). Defaults\n * to `globalThis.WebSocket`. */\n WebSocket?: MeshSignalingClientOptions[\"WebSocket\"];\n /** Forwarded error callback for diagnostic UI. */\n onError?: MeshSignalingClientOptions[\"onError\"];\n };\n /** WebRTC configuration. On browsers the defaults are fine; in Node or\n * Bun pass the `RTCPeerConnection` ctor from `werift` or `@roamhq/wrtc`. */\n rtc?: {\n RTCPeerConnection?: MeshWebRTCAdapterOptions[\"RTCPeerConnection\"];\n iceServers?: RTCIceServer[];\n dataChannelLabel?: string;\n };\n /** The local peer's keyring — either a fully-constructed instance, or a\n * persistence adapter to load one from. When a storage adapter is given\n * and `storage.load()` resolves to `null`, the factory throws with a\n * message pointing at the bootstrap helper in `@fairfox/polly/mesh/node`;\n * we deliberately do not generate an identity silently. */\n keyring: MeshKeyring | { storage: KeyringStorage };\n /** Optional Automerge-Repo storage adapter. Applications that want\n * durable local state pass an IndexedDB adapter in browsers or a\n * filesystem adapter in Node; omitting it keeps the Repo in-memory. */\n repoStorage?: StorageAdapterInterface;\n /** When `false`, signs but does not encrypt. Defaults to `true` — the\n * full $meshState posture where the server is off the data path. */\n encryptionEnabled?: boolean;\n}\n\n/** Handle returned by {@link createMeshClient}. */\nexport interface MeshClient {\n /** The Automerge Repo. `$meshState` has already been configured against\n * this repo, so primitives just work — but the repo is exposed in case\n * the application needs it directly (server-side cron, bulk exports,\n * migration tools). */\n repo: Repo;\n /** The configured keyring. Exposed so the application can inspect or\n * mutate it (add authorised peers, apply revocations) and then\n * re-persist via its storage. */\n keyring: MeshKeyring;\n /** The signalling client. Exposed for applications that need to hook\n * lifecycle events or send custom signalling payloads. */\n signaling: MeshSignalingClient;\n /** The WebRTC network adapter. Exposed for advanced use (blob store\n * wiring, peer-connection introspection). */\n networkAdapter: MeshNetworkAdapter;\n /** The underlying WebRTC adapter wrapped by {@link networkAdapter}. */\n webrtcAdapter: MeshWebRTCAdapter;\n /** Close the signalling WebSocket, tear down every RTCPeerConnection,\n * and shut the Repo cleanly. Idempotent. */\n close(): Promise<void>;\n}\n\n/**\n * Construct a fully-wired mesh client. Resolves once the signalling\n * connection is open and the Repo is ready to mutate documents; WebRTC\n * peer connections negotiate asynchronously in the background.\n */\nexport async function createMeshClient(options: CreateMeshClientOptions): Promise<MeshClient> {\n const keyring = await resolveKeyring(options.keyring);\n const encryptionEnabled = options.encryptionEnabled ?? true;\n\n // A mesh keyring must carry the per-Repo document key used by\n // MeshNetworkAdapter's encryption layer. We fail loud rather than\n // silently disable encryption when encryptionEnabled is true.\n if (encryptionEnabled && !keyring.documentKeys.has(DEFAULT_MESH_KEY_ID)) {\n throw new Error(\n `createMeshClient: encryption is enabled but the keyring has no document key for \"${DEFAULT_MESH_KEY_ID}\". Bootstrap or apply a pairing token that carries the document key before connecting.`\n );\n }\n\n const knownPeerIds = [...keyring.knownPeers.keys()].filter(\n (id) => id !== options.signaling.peerId\n );\n\n const webrtcAdapterOptions: MeshWebRTCAdapterOptions = {\n signaling: undefined as unknown as MeshSignalingClient, // wired after signaling construction\n peerId: options.signaling.peerId,\n knownPeerIds,\n ...(options.rtc?.iceServers !== undefined && { iceServers: options.rtc.iceServers }),\n ...(options.rtc?.dataChannelLabel !== undefined && {\n dataChannelLabel: options.rtc.dataChannelLabel,\n }),\n ...(options.rtc?.RTCPeerConnection !== undefined && {\n RTCPeerConnection: options.rtc.RTCPeerConnection,\n }),\n };\n\n // The signalling client needs a handleSignal callback, but that callback\n // lives on the WebRTC adapter — which itself wants a reference to the\n // signalling client for sending answers. Break the cycle by letting the\n // signalling client's onSignal reach the adapter through a closure over\n // `webrtcAdapter`, which is assigned immediately below.\n let webrtcAdapter: MeshWebRTCAdapter | undefined;\n const signaling = new MeshSignalingClient({\n url: options.signaling.url,\n peerId: options.signaling.peerId,\n ...(options.signaling.WebSocket !== undefined && { WebSocket: options.signaling.WebSocket }),\n ...(options.signaling.onError !== undefined && { onError: options.signaling.onError }),\n onSignal: (fromPeerId, payload) => {\n webrtcAdapter?.handleSignal(fromPeerId, payload);\n },\n });\n\n webrtcAdapterOptions.signaling = signaling;\n webrtcAdapter = new MeshWebRTCAdapter(webrtcAdapterOptions);\n\n const networkAdapter = new MeshNetworkAdapter({\n base: webrtcAdapter,\n keyring,\n encryptionEnabled,\n });\n\n const repo = new Repo({\n network: [networkAdapter],\n ...(options.repoStorage !== undefined && { storage: options.repoStorage }),\n });\n\n configureMeshState(repo);\n\n await signaling.connect();\n\n return {\n repo,\n keyring,\n signaling,\n networkAdapter,\n webrtcAdapter,\n close: async () => {\n signaling.close();\n webrtcAdapter?.disconnect();\n await repo.shutdown();\n },\n };\n}\n\nasync function resolveKeyring(\n source: MeshKeyring | { storage: KeyringStorage }\n): Promise<MeshKeyring> {\n if (\"storage\" in source) {\n const loaded = await source.storage.load();\n if (loaded === null) {\n throw new Error(\n \"createMeshClient: keyring storage returned null (no saved keyring). In a Node CLI, bootstrap with `bootstrapCliKeyring` from `@fairfox/polly/mesh/node`; in a browser, run your pairing flow first and save the keyring through the storage adapter before constructing the client.\"\n );\n }\n return loaded;\n }\n return source;\n}\n",
|
|
14
|
+
"/**\n * mesh-network-adapter — Phase 2 wrapping NetworkAdapter that adds Polly's\n * mesh-transport semantics on top of any underlying Automerge NetworkAdapter.\n *\n * The mesh transport's job is to make every message between peers signed\n * and encrypted before it reaches the wire. Rather than reimplementing the\n * Automerge sync protocol, this adapter takes a base adapter (in production\n * a real WebRTC or WebSocket adapter; in tests an in-memory loopback) and\n * applies the crypto envelope to every message that flows through.\n *\n * Outgoing path (Repo → wire):\n * 1. The Repo's NetworkSubsystem calls send(message) on this adapter.\n * 2. We serialise the message to bytes, encrypt them under the local\n * keyring's document key, sign the resulting blob with the local\n * identity's secret key, and pack the pair into a MeshFrame.\n * 3. We hand the MeshFrame off to the base adapter, which puts it on\n * whatever wire it owns.\n *\n * Incoming path (wire → Repo):\n * 1. The base adapter emits a 'message' event with bytes from the wire.\n * 2. We unpack the MeshFrame, look up the sender's public key in the\n * keyring, verify the signature, look up the document key, decrypt\n * the payload, and deserialise it back to the original message.\n * 3. We re-emit the 'message' event upward to the Repo's NetworkSubsystem\n * with the decrypted message.\n *\n * The keyring is an injected dependency. In production it's backed by\n * persistent storage and populated through the pairing flow. For tests it\n * is just a Map of publicly-known fixtures that both sides share.\n *\n * Caveat for the Phase 2 first cut: Automerge sync messages don't have a\n * stable \"what document does this belong to\" field at the wire level (the\n * documentId is part of the message contents). The mesh adapter therefore\n * uses a single per-Repo encryption key for now rather than per-document\n * keys, and stores the key once in the keyring under the well-known id\n * \"polly-mesh-default\". The plan describes per-document keys as the right\n * end state; that requires either parsing the message to extract the\n * documentId before encrypting (peeking inside the binary protocol) or\n * threading the document context through the network subsystem (which\n * needs upstream support). A follow-up will address this.\n */\n\nimport {\n type Message,\n NetworkAdapter,\n type PeerId,\n type PeerMetadata,\n} from \"@automerge/automerge-repo/slim\";\nimport {\n decodeEncryptedEnvelope,\n encodeEncryptedEnvelope,\n openEnvelope as openEncryptedEnvelope,\n sealEnvelope as sealEncryptedEnvelope,\n} from \"./encryption\";\nimport {\n decodeSignedEnvelope,\n encodeSignedEnvelope,\n openEnvelope as openSignedEnvelope,\n type SigningKeyPair,\n signEnvelope,\n} from \"./signing\";\n\n/** The well-known document id used for the Phase 2 first-cut single-key\n * encryption mode. See the file-level comment for the per-document key\n * follow-up. */\nexport const DEFAULT_MESH_KEY_ID = \"polly-mesh-default\";\n\n/**\n * A mesh keyring holds the local peer's signing identity, the public keys\n * of every peer the local node will accept messages from, the symmetric\n * encryption keys for documents the local node has access to, and the set\n * of peers whose keys have been revoked.\n */\nexport interface MeshKeyring {\n /** The local peer's signing keypair. The secret never leaves this\n * keyring; the public key is gossiped through the access set. */\n identity: SigningKeyPair;\n /** Map from peer id (string) to that peer's signing public key. The\n * mesh adapter rejects messages from peers not present in this map. */\n knownPeers: Map<string, Uint8Array>;\n /** Map from document key id (typically the documentId, or the well-known\n * default for the single-key first cut) to the symmetric encryption key. */\n documentKeys: Map<string, Uint8Array>;\n /** Set of peer ids whose keys have been revoked. The mesh adapter drops\n * incoming messages from any peer in this set, even if the peer is still\n * present in {@link knownPeers}. Revocation is applied via the revocation\n * module; the set is kept separate from knownPeers so that an application\n * can audit who was once authorised without losing the revocation record. */\n revokedPeers: Set<string>;\n /** Optional set of peer ids authorised to issue revocations. When present\n * and non-empty, `decodeRevocation` accepts a signed record only if the\n * issuer is in this set. When undefined or empty, any signed revocation\n * from a known peer is accepted (the Phase 2 first-cut default). This\n * field layers a \"who can revoke whom\" check on top of the signature\n * layer without breaking existing callers. */\n revocationAuthority?: Set<string>;\n}\n\n/**\n * Constructor options for {@link MeshNetworkAdapter}.\n */\nexport interface MeshNetworkAdapterOptions {\n /** The underlying NetworkAdapter that puts crypto-wrapped bytes on the\n * wire. In production this is a WebRTC or WebSocket adapter; in tests\n * it's an in-memory loopback. */\n base: NetworkAdapter;\n /** The local node's keyring. The adapter signs every outgoing message\n * with `identity.secretKey` and verifies every incoming message against\n * the public keys in `knownPeers`. */\n keyring: MeshKeyring;\n /** When false, the adapter signs but does not encrypt. Outgoing messages\n * carry a signature envelope but the payload is plaintext; incoming\n * messages are verified against the sender's public key without a\n * decryption step. This mode is used by $peerState's `sign: true`\n * option, where the server must still be able to parse Automerge sync\n * messages. Defaults to true (encrypt + sign, the full $meshState\n * posture). */\n encryptionEnabled?: boolean;\n}\n\n/**\n * NetworkAdapter that wraps another adapter with Polly's mesh-transport\n * crypto envelope. Every outgoing message is encrypted then signed; every\n * incoming message is verified then decrypted before being forwarded to\n * the Repo's network subsystem.\n *\n * The adapter delegates lifecycle (connect, disconnect, isReady, peer\n * discovery) to the base adapter unchanged. Only the message body is\n * intercepted.\n */\nexport class MeshNetworkAdapter extends NetworkAdapter {\n readonly base: NetworkAdapter;\n readonly keyring: MeshKeyring;\n readonly encryptionEnabled: boolean;\n\n constructor(options: MeshNetworkAdapterOptions) {\n super();\n this.base = options.base;\n this.keyring = options.keyring;\n this.encryptionEnabled = options.encryptionEnabled ?? true;\n\n // Forward lifecycle and peer events from the base adapter.\n this.base.on(\"close\", () => this.emit(\"close\"));\n this.base.on(\"peer-candidate\", (payload) => this.emit(\"peer-candidate\", payload));\n this.base.on(\"peer-disconnected\", (payload) => this.emit(\"peer-disconnected\", payload));\n\n // Intercept incoming messages: the base adapter will surface them as\n // 'message' events with crypto-wrapped payloads. We unwrap and re-emit.\n this.base.on(\"message\", (rawMessage) => {\n const unwrapped = this.tryUnwrap(rawMessage);\n if (unwrapped) {\n this.emit(\"message\", unwrapped);\n }\n // Silently drop messages that fail verification or decryption. A\n // production adapter would surface this through a diagnostic channel\n // so the application could prompt the user; the Phase 2 first cut\n // logs through the standard \"drop unknown\" semantics of the network\n // subsystem.\n });\n }\n\n isReady(): boolean {\n return this.base.isReady();\n }\n\n whenReady(): Promise<void> {\n return this.base.whenReady();\n }\n\n connect(peerId: PeerId, peerMetadata?: PeerMetadata): void {\n this.peerId = peerId;\n if (peerMetadata !== undefined) {\n this.peerMetadata = peerMetadata;\n }\n this.base.connect(peerId, peerMetadata);\n }\n\n disconnect(): void {\n this.base.disconnect();\n }\n\n send(message: Message): void {\n const wrapped = this.wrap(message);\n this.base.send(wrapped);\n }\n\n /**\n * Wrap an outgoing Automerge message in an encrypt-then-sign envelope.\n * The wrapped payload is returned as a Message with the original sender\n * and target ids and the crypto blob in the `data` field.\n */\n private wrap(message: Message): Message {\n const serialised = serialiseMessage(message);\n\n let payloadToSign: Uint8Array;\n if (this.encryptionEnabled) {\n const docKey = this.keyring.documentKeys.get(DEFAULT_MESH_KEY_ID);\n if (!docKey) {\n throw new Error(\n `MeshNetworkAdapter: missing document encryption key under id \"${DEFAULT_MESH_KEY_ID}\". Provision the key in the keyring before sending.`\n );\n }\n const encrypted = sealEncryptedEnvelope(serialised, DEFAULT_MESH_KEY_ID, docKey);\n payloadToSign = encodeEncryptedEnvelope(encrypted);\n } else {\n payloadToSign = serialised;\n }\n\n const signed = signEnvelope(payloadToSign, message.senderId, this.keyring.identity.secretKey);\n const signedBytes = encodeSignedEnvelope(signed);\n\n return {\n type: message.type,\n senderId: message.senderId,\n targetId: message.targetId,\n data: signedBytes,\n } as unknown as Message;\n }\n\n /**\n * Try to unwrap an incoming crypto-wrapped message. Returns the original\n * Message on success, undefined on verification or decryption failure.\n */\n private tryUnwrap(message: Message): Message | undefined {\n if (!message.data) return undefined;\n\n let signed: ReturnType<typeof decodeSignedEnvelope>;\n try {\n signed = decodeSignedEnvelope(message.data);\n } catch {\n return undefined;\n }\n\n // Drop messages from peers whose keys have been revoked, even if the\n // public key is still present in knownPeers. The revocation set is the\n // authoritative \"this peer is no longer trusted\" marker.\n if (this.keyring.revokedPeers.has(signed.senderId)) {\n return undefined;\n }\n\n const senderKey = this.keyring.knownPeers.get(signed.senderId);\n if (!senderKey) {\n return undefined;\n }\n\n let verifiedPayload: Uint8Array;\n try {\n verifiedPayload = openSignedEnvelope(signed, senderKey);\n } catch {\n return undefined;\n }\n\n if (!this.encryptionEnabled) {\n // Sign-only mode: the verified payload IS the serialised message.\n return deserialiseMessage(verifiedPayload);\n }\n\n // Full encrypt+sign mode: unwrap the encryption envelope.\n let encrypted: ReturnType<typeof decodeEncryptedEnvelope>;\n try {\n encrypted = decodeEncryptedEnvelope(verifiedPayload);\n } catch {\n return undefined;\n }\n\n const docKey = this.keyring.documentKeys.get(encrypted.documentId);\n if (!docKey) {\n return undefined;\n }\n\n let plaintext: Uint8Array;\n try {\n plaintext = openEncryptedEnvelope(encrypted, docKey);\n } catch {\n return undefined;\n }\n\n return deserialiseMessage(plaintext);\n }\n}\n\n// ─── message serialisation ─────────────────────────────────────────────────\n\n/**\n * Serialise an Automerge sync message to a binary blob suitable for\n * encryption. The format is a length-prefixed JSON header (carrying the\n * non-binary fields) followed by the raw `data` bytes (which Automerge's\n * sync messages carry as Uint8Array). This avoids round-tripping the\n * binary payload through JSON, which would balloon its size.\n */\nfunction serialiseMessage(message: Message): Uint8Array {\n const headerObj: Record<string, unknown> = {\n type: message.type,\n senderId: message.senderId,\n targetId: message.targetId,\n };\n if (\"documentId\" in message && message.documentId !== undefined) {\n headerObj[\"documentId\"] = message.documentId;\n }\n if (\"count\" in message && message.count !== undefined) {\n headerObj[\"count\"] = message.count;\n }\n if (\"sessionId\" in message && message.sessionId !== undefined) {\n headerObj[\"sessionId\"] = message.sessionId;\n }\n const headerBytes = new TextEncoder().encode(JSON.stringify(headerObj));\n const dataBytes: Uint8Array =\n \"data\" in message && message.data instanceof Uint8Array ? message.data : new Uint8Array(0);\n\n const out = new Uint8Array(4 + headerBytes.length + dataBytes.length);\n const view = new DataView(out.buffer);\n view.setUint32(0, headerBytes.length, false);\n out.set(headerBytes, 4);\n out.set(dataBytes, 4 + headerBytes.length);\n return out;\n}\n\n/**\n * Inverse of {@link serialiseMessage}.\n */\nfunction deserialiseMessage(bytes: Uint8Array): Message {\n if (bytes.length < 4) {\n throw new Error(\"MeshNetworkAdapter: message too short to deserialise.\");\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const headerLen = view.getUint32(0, false);\n if (bytes.length < 4 + headerLen) {\n throw new Error(\"MeshNetworkAdapter: message header truncated.\");\n }\n const header = JSON.parse(new TextDecoder().decode(bytes.subarray(4, 4 + headerLen)));\n const data = bytes.slice(4 + headerLen);\n return { ...header, data } as unknown as Message;\n}\n",
|
|
7
15
|
"/**\n * signing — Ed25519 signing and verification for Polly's $meshState\n * primitive (Phase 2). Wraps tweetnacl with a small Polly-flavoured API\n * so the rest of the codebase never imports tweetnacl directly.\n *\n * Every operation that flows through a $meshState transport is signed by\n * the originating peer's private key before transmission and verified by\n * every receiving peer against a known public-key set before being applied.\n * This is the Byzantine-tolerance mechanism: a peer whose private key is\n * compromised can be revoked through a further signed operation, after\n * which honest peers reject anything signed by the revoked key.\n *\n * tweetnacl uses the Ed25519 curve. Public keys and signatures are 32 and\n * 64 bytes respectively, which keeps the per-op overhead small enough that\n * signing every Automerge sync message is feasible even on mobile.\n *\n * The shape of the wrapper:\n *\n * - {@link generateSigningKeyPair} produces a new Ed25519 keypair. The\n * private key never leaves the device that generated it; the public\n * key is gossiped through the access set.\n *\n * - {@link sign} produces a 64-byte detached signature over a payload.\n *\n * - {@link verify} checks a payload against a signature and a public\n * key. Returns boolean rather than throwing so call sites can handle\n * verification failure as a normal control-flow case.\n *\n * - {@link signEnvelope} and {@link openEnvelope} package payload + sender\n * id + signature into a single binary envelope, which is what the mesh\n * network adapter actually puts on the wire.\n */\n\nimport nacl from \"tweetnacl\";\n\n/** Length in bytes of an Ed25519 public key. */\nexport const PUBLIC_KEY_BYTES = 32;\n/** Length in bytes of an Ed25519 secret (private) key. */\nexport const SECRET_KEY_BYTES = 64;\n/** Length in bytes of an Ed25519 detached signature. */\nexport const SIGNATURE_BYTES = 64;\n\n/**\n * An Ed25519 keypair. The {@link publicKey} is safe to share with peers;\n * the {@link secretKey} must never leave the device.\n */\nexport interface SigningKeyPair {\n publicKey: Uint8Array;\n secretKey: Uint8Array;\n}\n\n/**\n * A signed envelope. The wire format is the concatenation of the sender id\n * length, the sender id bytes, the signature, and the payload. Callers\n * shouldn't rely on the exact layout — use {@link signEnvelope} and\n * {@link openEnvelope} to round-trip.\n */\nexport interface SignedEnvelope {\n /** Stable sender peer identifier (UTF-8 string). The receiving side uses\n * this to look up the sender's public key in the document's access set. */\n senderId: string;\n /** The original payload bytes, untouched. */\n payload: Uint8Array;\n /** 64-byte Ed25519 signature over the payload. */\n signature: Uint8Array;\n}\n\n/** Errors thrown by the signing subsystem. */\nexport class SigningError extends Error {\n readonly code:\n | \"invalid-secret-key\"\n | \"invalid-public-key\"\n | \"invalid-signature-length\"\n | \"envelope-malformed\";\n\n constructor(message: string, code: SigningError[\"code\"]) {\n super(message);\n this.name = \"SigningError\";\n this.code = code;\n }\n}\n\n/**\n * Generate a fresh Ed25519 keypair. Calls into tweetnacl's CSPRNG.\n */\nexport function generateSigningKeyPair(): SigningKeyPair {\n const pair = nacl.sign.keyPair();\n return {\n publicKey: pair.publicKey,\n secretKey: pair.secretKey,\n };\n}\n\n/**\n * Reconstruct a keypair from an existing 64-byte secret key. Useful for\n * loading keys from persistent storage. Throws if the key is the wrong size.\n */\nexport function signingKeyPairFromSecret(secretKey: Uint8Array): SigningKeyPair {\n if (secretKey.length !== SECRET_KEY_BYTES) {\n throw new SigningError(\n `Ed25519 secret key must be ${SECRET_KEY_BYTES} bytes, got ${secretKey.length}.`,\n \"invalid-secret-key\"\n );\n }\n const pair = nacl.sign.keyPair.fromSecretKey(secretKey);\n return {\n publicKey: pair.publicKey,\n secretKey: pair.secretKey,\n };\n}\n\n/**\n * Produce a 64-byte detached signature over the given payload using the\n * supplied secret key.\n */\nexport function sign(payload: Uint8Array, secretKey: Uint8Array): Uint8Array {\n if (secretKey.length !== SECRET_KEY_BYTES) {\n throw new SigningError(\n `Ed25519 secret key must be ${SECRET_KEY_BYTES} bytes, got ${secretKey.length}.`,\n \"invalid-secret-key\"\n );\n }\n return nacl.sign.detached(payload, secretKey);\n}\n\n/**\n * Verify a detached signature against a payload and a public key. Returns\n * true if the signature is valid, false otherwise. Wrong-length keys or\n * signatures throw {@link SigningError} so callers can distinguish a bad\n * signature from a misshapen input.\n */\nexport function verify(payload: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean {\n if (publicKey.length !== PUBLIC_KEY_BYTES) {\n throw new SigningError(\n `Ed25519 public key must be ${PUBLIC_KEY_BYTES} bytes, got ${publicKey.length}.`,\n \"invalid-public-key\"\n );\n }\n if (signature.length !== SIGNATURE_BYTES) {\n throw new SigningError(\n `Ed25519 signature must be ${SIGNATURE_BYTES} bytes, got ${signature.length}.`,\n \"invalid-signature-length\"\n );\n }\n return nacl.sign.detached.verify(payload, signature, publicKey);\n}\n\n/**\n * Sign a payload and pack it into a {@link SignedEnvelope} along with the\n * sender id. The mesh network adapter calls this on every outgoing message\n * before handing it to the transport.\n */\nexport function signEnvelope(\n payload: Uint8Array,\n senderId: string,\n secretKey: Uint8Array\n): SignedEnvelope {\n const signature = sign(payload, secretKey);\n return { senderId, payload, signature };\n}\n\n/**\n * Verify a {@link SignedEnvelope} against the sender's known public key.\n * Returns the inner payload on success, throws on failure. The mesh\n * network adapter calls this on every incoming message before forwarding\n * the payload to the underlying Automerge sync subsystem.\n */\nexport function openEnvelope(envelope: SignedEnvelope, publicKey: Uint8Array): Uint8Array {\n const ok = verify(envelope.payload, envelope.signature, publicKey);\n if (!ok) {\n throw new SigningError(\n `Signature verification failed for envelope from ${envelope.senderId}.`,\n \"envelope-malformed\"\n );\n }\n return envelope.payload;\n}\n\n/**\n * Serialise a {@link SignedEnvelope} to a single binary blob suitable for\n * transmission over a network adapter. Wire format:\n *\n * [4 bytes BE: senderId byte length]\n * [N bytes: senderId UTF-8]\n * [64 bytes: signature]\n * [remaining: payload]\n *\n * Callers should not depend on the exact bytes — they should round-trip\n * through {@link encodeSignedEnvelope} / {@link decodeSignedEnvelope}.\n */\nexport function encodeSignedEnvelope(envelope: SignedEnvelope): Uint8Array {\n const senderBytes = new TextEncoder().encode(envelope.senderId);\n const total = 4 + senderBytes.length + SIGNATURE_BYTES + envelope.payload.length;\n const out = new Uint8Array(total);\n const view = new DataView(out.buffer);\n view.setUint32(0, senderBytes.length, false);\n out.set(senderBytes, 4);\n out.set(envelope.signature, 4 + senderBytes.length);\n out.set(envelope.payload, 4 + senderBytes.length + SIGNATURE_BYTES);\n return out;\n}\n\n/**\n * Deserialise a binary envelope produced by {@link encodeSignedEnvelope}.\n * Throws on malformed input.\n */\nexport function decodeSignedEnvelope(bytes: Uint8Array): SignedEnvelope {\n if (bytes.length < 4 + SIGNATURE_BYTES) {\n throw new SigningError(\n `Envelope too short: ${bytes.length} bytes, need at least ${4 + SIGNATURE_BYTES}.`,\n \"envelope-malformed\"\n );\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const senderLen = view.getUint32(0, false);\n if (bytes.length < 4 + senderLen + SIGNATURE_BYTES) {\n throw new SigningError(\n `Envelope truncated: declared sender length ${senderLen}, total ${bytes.length}.`,\n \"envelope-malformed\"\n );\n }\n const senderId = new TextDecoder().decode(bytes.subarray(4, 4 + senderLen));\n const signature = bytes.slice(4 + senderLen, 4 + senderLen + SIGNATURE_BYTES);\n const payload = bytes.slice(4 + senderLen + SIGNATURE_BYTES);\n return { senderId, payload, signature };\n}\n",
|
|
8
|
-
"/**\n * mesh-signaling-client — browser-side client for Polly's signalingServer\n * Elysia plugin. Connects to the signalling WebSocket, registers a peer\n * id, and relays SDP/ICE messages between local WebRTC connections and\n * remote peers.\n *\n * This module is the companion to {@link signalingServer} from the Elysia\n * plugin family. The server accepts \"join\" and \"signal\" messages; this\n * client produces them. The protocol matches: opening the connection,\n * sending a \"join\" with the local peer id, and then using sendSignal()\n * to forward SDP and ICE messages to specific target peers.\n *\n * Because this client is browser-only in its first incarnation — it\n * assumes the global `WebSocket` is available — it cannot be exercised\n * under bun:test the way the server-side plugin is. The first validation\n * of this code path is either a Playwright harness or a human running\n * the browser-side example that consumes it.\n */\n\n/** A signal message either sent to or received from the signalling server.\n * Matches the wire format produced by the Elysia signalingServer plugin. */\nexport interface SignalingMessage {\n type: \"join\" | \"signal\" | \"error\";\n peerId?: string;\n targetPeerId?: string;\n payload?: unknown;\n reason?: \"unknown-target\" | \"not-joined\" | \"malformed\";\n}\n\n/** Options for constructing a {@link MeshSignalingClient}. */\nexport interface MeshSignalingClientOptions {\n /** The signalling server URL (ws:// or wss://). */\n url: string;\n /** The local peer id that this client will register with on join. */\n peerId: string;\n /** Callback invoked whenever a signal message from another peer arrives.\n * The receiver dispatches to the right PeerConnection based on the\n * `fromPeerId`. */\n onSignal: (fromPeerId: string, payload: unknown) => void;\n /** Optional callback invoked when the server returns an error (for\n * diagnostic UI or reconnection logic). */\n onError?: (reason: string, targetPeerId?: string) => void;\n /** Optional callback for the open and close lifecycle events. */\n onOpen?: () => void;\n onClose?: () => void;\n}\n\n/**\n * Thin wrapper around a WebSocket connection to a Polly signalling server.\n * Handles the join handshake, routes incoming signals to the supplied\n * callback, and exposes a {@link sendSignal} method for outgoing signals.\n *\n * This class is deliberately small. It has no opinion on the signal\n * payload shape (the wire carries it as `unknown`), so it can carry SDP\n * offers, SDP answers, ICE candidates, or any other message the\n * WebRTC adapter wants to exchange with peers.\n */\nexport class MeshSignalingClient {\n readonly url: string;\n readonly peerId: string;\n private readonly onSignal: (fromPeerId: string, payload: unknown) => void;\n private readonly onError?: (reason: string, targetPeerId?: string) => void;\n private readonly onOpen?: () => void;\n private readonly onClose?: () => void;\n private socket: WebSocket | undefined;\n private joined = false;\n\n constructor(options: MeshSignalingClientOptions) {\n this.url = options.url;\n this.peerId = options.peerId;\n this.onSignal = options.onSignal;\n if (options.onError !== undefined) this.onError = options.onError;\n if (options.onOpen !== undefined) this.onOpen = options.onOpen;\n if (options.onClose !== undefined) this.onClose = options.onClose;\n }\n\n /**\n * Open the WebSocket and send the join message. Resolves once the\n * connection is open; callers should not send signals before this\n * promise resolves.\n */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const ws = new
|
|
9
|
-
"/**\n * crdt-specialised — text, counter, and list variants for Polly's peer-first\n * state primitives.\n *\n * Where the base $crdtState binds a signal to a whole Automerge document with\n * naive top-level structural assignment, the specialised variants each own a\n * single field inside their document and use type-specific Automerge\n * operations to mutate it. This is the difference between \"your write\n * survives a concurrent edit\" and \"your write clobbers the other peer's\n * edit,\" and it matters most for text editing where every keystroke from a\n * concurrent peer would otherwise be lost.\n *\n * - **$crdtText** stores its value in `doc.text` and writes via\n * `Automerge.updateText`, which computes the minimal sequence of character\n * splices between the previous and new strings and records each one as a\n * CRDT operation. Concurrent text edits merge.\n *\n * - **$crdtCounter** stores its value in `doc.count` as an `Automerge.Counter`\n * instance and writes via `counter.increment(delta)`. Increments commute\n * across peers, so two clients each adding 1 to a counter at 5 produce a\n * counter at 7 after merging — last-write-wins semantics on a plain number\n * would lose one of the increments.\n *\n * - **$crdtList** stores its value in `doc.items` and, for the Phase 0 cut,\n * uses naive whole-array replacement on every write. This is correct for\n * single-writer scenarios and good enough to exercise the rest of the\n * pipeline. Phase 1 will replace the write path with proper diff-to-splice\n * logic so that concurrent inserts and removes preserve list ordering.\n *\n * All three variants share a single internal factory that handles the\n * primitive-registry guard, migration-registry guard, schema-version\n * migration, hydration promise, two-way binding with an `updating` flag,\n * and the {@link MigratableState} interface. Variants differ only in the\n * `extractValue` and `applyWrite` hooks they pass.\n */\n\nimport { Counter, type DocHandle, updateText } from \"@automerge/automerge-repo\";\nimport { effect, signal } from \"@preact/signals\";\nimport type { Access } from \"./access\";\nimport { type MigratableState, MigrationError, migrationRegistry } from \"./migrate-primitive\";\nimport { type PrimitiveKind, primitiveRegistry } from \"./primitive-registry\";\nimport { type Migrations, runMigrations, setDocVersion, type VersionedDoc } from \"./schema-version\";\n\n/**\n * Public interface for a specialised primitive. The signal value type V is\n * not the same as the underlying Automerge document — for example, $crdtText\n * exposes a string signal whose value lives at `doc.text` inside the\n * document. Implements {@link MigratableState} so the cross-primitive\n * migration helper can consume it directly.\n */\nexport interface SpecialisedPrimitive<V> extends MigratableState<V> {\n readonly key: string;\n readonly primitive: PrimitiveKind;\n value: V;\n readonly loaded: Promise<void>;\n readonly handle: DocHandle<unknown> | undefined;\n}\n\n/** Internal config shape consumed by {@link createSpecialisedPrimitive}. */\ninterface SpecialisedConfig<V, D extends VersionedDoc> {\n key: string;\n primitive: PrimitiveKind;\n initialValue: V;\n getHandle: () => Promise<DocHandle<D>>;\n /** Read the typed signal value out of the Automerge document. Called once\n * on hydration and again on every remote change. */\n extractValue: (doc: D) => V;\n /** Apply a typed signal value to the Automerge document inside the\n * `handle.change` block. Called every time the local signal is reassigned. */\n applyWrite: (doc: D, value: V) => void;\n schemaVersion?: number;\n migrations?: Migrations;\n access?: Access;\n callSite?: string;\n}\n\n/**\n * Shared factory. Identical guard, hydration, and binding wiring across all\n * three specialised variants; the variants differ only in the extract and\n * apply hooks they supply.\n */\nfunction createSpecialisedPrimitive<V, D extends VersionedDoc>(\n config: SpecialisedConfig<V, D>\n): SpecialisedPrimitive<V> {\n if (migrationRegistry.isMarked(config.key, config.primitive)) {\n throw new MigrationError(\n `Cannot construct $${config.primitive}(\"${config.key}\"): this key has been marked as migrated. Migrations are one-way; use the destination primitive instead.`,\n \"already-migrated\",\n config.key,\n config.primitive\n );\n }\n primitiveRegistry.register(config.key, config.primitive, config.callSite);\n\n const inner = signal<V>(config.initialValue);\n let updating = false;\n let currentHandle: DocHandle<D> | undefined;\n\n const loaded = (async () => {\n const handle = await config.getHandle();\n await handle.whenReady();\n currentHandle = handle;\n\n if (config.schemaVersion !== undefined) {\n const targetVersion = config.schemaVersion;\n const migrations = config.migrations ?? {};\n handle.change((doc) => {\n runMigrations(doc as unknown as Record<string, unknown>, targetVersion, migrations);\n setDocVersion(doc as unknown as Record<string, unknown>, targetVersion);\n });\n }\n\n updating = true;\n try {\n inner.value = config.extractValue(handle.doc());\n } finally {\n updating = false;\n }\n\n handle.on(\"change\", (payload) => {\n if (updating) return;\n updating = true;\n try {\n inner.value = config.extractValue(payload.doc);\n } finally {\n updating = false;\n }\n });\n\n effect(() => {\n const value = inner.value;\n if (updating) return;\n if (!currentHandle) return;\n updating = true;\n try {\n currentHandle.change((doc) => {\n config.applyWrite(doc, value);\n });\n } finally {\n updating = false;\n }\n });\n })();\n\n return {\n key: config.key,\n primitive: config.primitive,\n get value() {\n return inner.value;\n },\n set value(next: V) {\n inner.value = next;\n },\n loaded,\n get handle() {\n return currentHandle as unknown as DocHandle<unknown> | undefined;\n },\n };\n}\n\n// ─── $crdtText ──────────────────────────────────────────────────────────────\n\n/** The Automerge document shape backing a $crdtText primitive. */\nexport type TextDoc = VersionedDoc & { text?: string };\n\n/** Options for {@link $crdtText}. */\nexport interface CrdtTextOptions {\n /** Primitive kind label. Defaults to \"peerState\"; Phase 2's $meshState\n * wrapper passes \"meshState\" instead. */\n primitive?: PrimitiveKind;\n /** Async factory that returns a ready DocHandle for the text document. */\n getHandle: () => Promise<DocHandle<TextDoc>>;\n schemaVersion?: number;\n migrations?: Migrations;\n access?: Access;\n callSite?: string;\n}\n\n/**\n * Create a CRDT-backed text primitive. The signal exposes a plain string;\n * writes are diffed into character-level splices via `Automerge.updateText`\n * so that concurrent edits from peers merge cleanly rather than clobbering\n * each other.\n */\nexport function $crdtText(\n key: string,\n initialValue: string,\n options: CrdtTextOptions\n): SpecialisedPrimitive<string> {\n return createSpecialisedPrimitive<string, TextDoc>({\n key,\n primitive: options.primitive ?? \"peerState\",\n initialValue,\n getHandle: options.getHandle,\n extractValue: (doc) => doc.text ?? \"\",\n applyWrite: (doc, value) => {\n if (doc.text === undefined) {\n // First write — seed the field. Subsequent writes will use updateText.\n (doc as unknown as TextDoc).text = value;\n } else {\n updateText(doc, [\"text\"], value);\n }\n },\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n callSite: options.callSite,\n });\n}\n\n// ─── $crdtCounter ───────────────────────────────────────────────────────────\n\n/** The Automerge document shape backing a $crdtCounter primitive. */\nexport type CounterDoc = VersionedDoc & { count?: Counter };\n\n/** Options for {@link $crdtCounter}. */\nexport interface CrdtCounterOptions {\n primitive?: PrimitiveKind;\n getHandle: () => Promise<DocHandle<CounterDoc>>;\n schemaVersion?: number;\n migrations?: Migrations;\n access?: Access;\n callSite?: string;\n}\n\n/**\n * Create a CRDT-backed counter primitive. The signal exposes a plain number;\n * writes compute the delta from the document's current value and call\n * `counter.increment(delta)` on the underlying `Automerge.Counter`. Concurrent\n * increments from peers commute, so two clients each adding 1 to a counter at\n * 5 produce a counter at 7 after merging.\n *\n * Application code that wants to express increments idiomatically can write\n * `counter.value = counter.value + 1`; the signal's reactivity captures the\n * read-then-write pattern and the factory translates it into a proper CRDT\n * increment operation underneath.\n */\nexport function $crdtCounter(\n key: string,\n initialValue: number,\n options: CrdtCounterOptions\n): SpecialisedPrimitive<number> {\n return createSpecialisedPrimitive<number, CounterDoc>({\n key,\n primitive: options.primitive ?? \"peerState\",\n initialValue,\n getHandle: options.getHandle,\n extractValue: (doc) => {\n const c = doc.count;\n if (c === undefined) return 0;\n return c.value;\n },\n applyWrite: (doc, value) => {\n const existing = doc.count;\n if (existing === undefined) {\n (doc as unknown as CounterDoc).count = new Counter(value);\n } else {\n const delta = value - existing.value;\n if (delta !== 0) {\n existing.increment(delta);\n }\n }\n },\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n callSite: options.callSite,\n });\n}\n\n// ─── $crdtList ──────────────────────────────────────────────────────────────\n\n/** The Automerge document shape backing a $crdtList primitive. */\nexport type ListDoc<T> = VersionedDoc & { items?: T[] };\n\n/** Options for {@link $crdtList}. */\nexport interface CrdtListOptions<T> {\n primitive?: PrimitiveKind;\n getHandle: () => Promise<DocHandle<ListDoc<T>>>;\n schemaVersion?: number;\n migrations?: Migrations;\n access?: Access;\n callSite?: string;\n}\n\n/**\n * Create a CRDT-backed list primitive. The signal exposes a plain array;\n * for the Phase 0 cut, writes replace the underlying array wholesale inside\n * an `Automerge.change` block. This is correct for single-writer scenarios\n * and is the simplest way to ship a working list primitive on the same\n * pipeline as text and counter.\n *\n * Phase 1 will replace the write path with proper structural diffing into\n * insert and remove operations so that concurrent edits from peers preserve\n * list ordering. Until then, applications using $crdtList in a multi-writer\n * setting should expect last-writer-wins semantics on the array as a whole.\n */\nexport function $crdtList<T>(\n key: string,\n initialValue: T[],\n options: CrdtListOptions<T>\n): SpecialisedPrimitive<T[]> {\n return createSpecialisedPrimitive<T[], ListDoc<T>>({\n key,\n primitive: options.primitive ?? \"peerState\",\n initialValue,\n getHandle: options.getHandle,\n extractValue: (doc) => (doc.items ? [...doc.items] : []),\n applyWrite: (doc, value) => {\n // Phase 0 naive replacement; see the function-level docstring for the\n // refinement plan.\n (doc as unknown as ListDoc<T>).items = [...value];\n },\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n callSite: options.callSite,\n });\n}\n",
|
|
16
|
+
"/**\n * mesh-signaling-client — browser-side client for Polly's signalingServer\n * Elysia plugin. Connects to the signalling WebSocket, registers a peer\n * id, and relays SDP/ICE messages between local WebRTC connections and\n * remote peers.\n *\n * This module is the companion to {@link signalingServer} from the Elysia\n * plugin family. The server accepts \"join\" and \"signal\" messages; this\n * client produces them. The protocol matches: opening the connection,\n * sending a \"join\" with the local peer id, and then using sendSignal()\n * to forward SDP and ICE messages to specific target peers.\n *\n * Because this client is browser-only in its first incarnation — it\n * assumes the global `WebSocket` is available — it cannot be exercised\n * under bun:test the way the server-side plugin is. The first validation\n * of this code path is either a Playwright harness or a human running\n * the browser-side example that consumes it.\n */\n\n/** A signal message either sent to or received from the signalling server.\n * Matches the wire format produced by the Elysia signalingServer plugin. */\nexport interface SignalingMessage {\n type: \"join\" | \"signal\" | \"error\";\n peerId?: string;\n targetPeerId?: string;\n payload?: unknown;\n reason?: \"unknown-target\" | \"not-joined\" | \"malformed\";\n}\n\n/** Options for constructing a {@link MeshSignalingClient}. */\nexport interface MeshSignalingClientOptions {\n /** The signalling server URL (ws:// or wss://). */\n url: string;\n /** The local peer id that this client will register with on join. */\n peerId: string;\n /** Callback invoked whenever a signal message from another peer arrives.\n * The receiver dispatches to the right PeerConnection based on the\n * `fromPeerId`. */\n onSignal: (fromPeerId: string, payload: unknown) => void;\n /** Optional callback invoked when the server returns an error (for\n * diagnostic UI or reconnection logic). */\n onError?: (reason: string, targetPeerId?: string) => void;\n /** Optional callback for the open and close lifecycle events. */\n onOpen?: () => void;\n onClose?: () => void;\n /** WebSocket constructor. Defaults to `globalThis.WebSocket`. Inject a\n * different implementation (e.g. `ws` package's `WebSocket`) when running\n * in an environment without a native WebSocket global, or to use a custom\n * subclass for tests or instrumentation. */\n WebSocket?: typeof WebSocket;\n}\n\n/**\n * Thin wrapper around a WebSocket connection to a Polly signalling server.\n * Handles the join handshake, routes incoming signals to the supplied\n * callback, and exposes a {@link sendSignal} method for outgoing signals.\n *\n * This class is deliberately small. It has no opinion on the signal\n * payload shape (the wire carries it as `unknown`), so it can carry SDP\n * offers, SDP answers, ICE candidates, or any other message the\n * WebRTC adapter wants to exchange with peers.\n */\nexport class MeshSignalingClient {\n readonly url: string;\n readonly peerId: string;\n private readonly onSignal: (fromPeerId: string, payload: unknown) => void;\n private readonly onError?: (reason: string, targetPeerId?: string) => void;\n private readonly onOpen?: () => void;\n private readonly onClose?: () => void;\n private socket: WebSocket | undefined;\n private joined = false;\n private readonly WebSocketCtor: typeof WebSocket;\n\n constructor(options: MeshSignalingClientOptions) {\n this.url = options.url;\n this.peerId = options.peerId;\n this.onSignal = options.onSignal;\n if (options.onError !== undefined) this.onError = options.onError;\n if (options.onOpen !== undefined) this.onOpen = options.onOpen;\n if (options.onClose !== undefined) this.onClose = options.onClose;\n const WS = options.WebSocket ?? globalThis.WebSocket;\n if (typeof WS !== \"function\") {\n throw new Error(\n \"MeshSignalingClient: no WebSocket implementation found. Pass one via options.WebSocket, or run in an environment where `globalThis.WebSocket` exists (Node 21+, Bun, browsers).\"\n );\n }\n this.WebSocketCtor = WS;\n }\n\n /**\n * Open the WebSocket and send the join message. Resolves once the\n * connection is open; callers should not send signals before this\n * promise resolves.\n */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const ws = new this.WebSocketCtor(this.url);\n this.socket = ws;\n\n ws.addEventListener(\"open\", () => {\n // Send the join message as the first frame. The server registers\n // the peer id and uses it as the authenticated sender for all\n // subsequent signals on this connection.\n ws.send(JSON.stringify({ type: \"join\", peerId: this.peerId } satisfies SignalingMessage));\n this.joined = true;\n this.onOpen?.();\n resolve();\n });\n\n ws.addEventListener(\"message\", (event) => {\n let msg: SignalingMessage;\n try {\n msg = typeof event.data === \"string\" ? JSON.parse(event.data) : event.data;\n } catch {\n return;\n }\n if (msg.type === \"signal\" && typeof msg.peerId === \"string\") {\n this.onSignal(msg.peerId, msg.payload);\n return;\n }\n if (msg.type === \"error\" && msg.reason) {\n this.onError?.(msg.reason, msg.targetPeerId);\n }\n });\n\n ws.addEventListener(\"error\", (err) => {\n reject(err);\n });\n\n ws.addEventListener(\"close\", () => {\n this.joined = false;\n this.onClose?.();\n });\n });\n }\n\n /**\n * Send a signal to another peer via the signalling server. The server\n * validates the sender (replacing the claimed peerId with the\n * authenticated join id) and routes to the target. Returns true if\n * the message was sent, false if the connection is not open.\n */\n sendSignal(targetPeerId: string, payload: unknown): boolean {\n if (!this.socket || this.socket.readyState !== this.WebSocketCtor.OPEN || !this.joined) {\n return false;\n }\n const msg: SignalingMessage = {\n type: \"signal\",\n peerId: this.peerId,\n targetPeerId,\n payload,\n };\n this.socket.send(JSON.stringify(msg));\n return true;\n }\n\n /**\n * Close the underlying WebSocket connection. The server's close handler\n * will evict this peer from its routing table.\n */\n close(): void {\n this.socket?.close();\n this.socket = undefined;\n this.joined = false;\n }\n\n /** True if the signalling connection is open and joined. */\n get isConnected(): boolean {\n return this.joined && this.socket?.readyState === this.WebSocketCtor.OPEN;\n }\n}\n",
|
|
17
|
+
"/**\n * crdt-specialised — text, counter, and list variants for Polly's peer-first\n * state primitives.\n *\n * Where the base $crdtState binds a signal to a whole Automerge document with\n * naive top-level structural assignment, the specialised variants each own a\n * single field inside their document and use type-specific Automerge\n * operations to mutate it. This is the difference between \"your write\n * survives a concurrent edit\" and \"your write clobbers the other peer's\n * edit,\" and it matters most for text editing where every keystroke from a\n * concurrent peer would otherwise be lost.\n *\n * - **$crdtText** stores its value in `doc.text` and writes via\n * `Automerge.updateText`, which computes the minimal sequence of character\n * splices between the previous and new strings and records each one as a\n * CRDT operation. Concurrent text edits merge.\n *\n * - **$crdtCounter** stores its value in `doc.count` as an `Automerge.Counter`\n * instance and writes via `counter.increment(delta)`. Increments commute\n * across peers, so two clients each adding 1 to a counter at 5 produce a\n * counter at 7 after merging — last-write-wins semantics on a plain number\n * would lose one of the increments.\n *\n * - **$crdtList** stores its value in `doc.items` and, for the Phase 0 cut,\n * uses naive whole-array replacement on every write. This is correct for\n * single-writer scenarios and good enough to exercise the rest of the\n * pipeline. Phase 1 will replace the write path with proper diff-to-splice\n * logic so that concurrent inserts and removes preserve list ordering.\n *\n * All three variants share a single internal factory that handles the\n * primitive-registry guard, migration-registry guard, schema-version\n * migration, hydration promise, two-way binding with an `updating` flag,\n * and the {@link MigratableState} interface. Variants differ only in the\n * `extractValue` and `applyWrite` hooks they pass.\n */\n\nimport { Counter, type DocHandle, updateText } from \"@automerge/automerge-repo/slim\";\nimport { effect, signal } from \"@preact/signals\";\nimport type { Access } from \"./access\";\nimport { type MigratableState, MigrationError, migrationRegistry } from \"./migrate-primitive\";\nimport { type PrimitiveKind, primitiveRegistry } from \"./primitive-registry\";\nimport { type Migrations, runMigrations, setDocVersion, type VersionedDoc } from \"./schema-version\";\n\n/**\n * Public interface for a specialised primitive. The signal value type V is\n * not the same as the underlying Automerge document — for example, $crdtText\n * exposes a string signal whose value lives at `doc.text` inside the\n * document. Implements {@link MigratableState} so the cross-primitive\n * migration helper can consume it directly.\n */\nexport interface SpecialisedPrimitive<V> extends MigratableState<V> {\n readonly key: string;\n readonly primitive: PrimitiveKind;\n value: V;\n readonly loaded: Promise<void>;\n readonly handle: DocHandle<unknown> | undefined;\n}\n\n/** Internal config shape consumed by {@link createSpecialisedPrimitive}. */\ninterface SpecialisedConfig<V, D extends VersionedDoc> {\n key: string;\n primitive: PrimitiveKind;\n initialValue: V;\n getHandle: () => Promise<DocHandle<D>>;\n /** Read the typed signal value out of the Automerge document. Called once\n * on hydration and again on every remote change. */\n extractValue: (doc: D) => V;\n /** Apply a typed signal value to the Automerge document inside the\n * `handle.change` block. Called every time the local signal is reassigned. */\n applyWrite: (doc: D, value: V) => void;\n schemaVersion?: number;\n migrations?: Migrations;\n access?: Access;\n callSite?: string;\n}\n\n/**\n * Shared factory. Identical guard, hydration, and binding wiring across all\n * three specialised variants; the variants differ only in the extract and\n * apply hooks they supply.\n */\nfunction createSpecialisedPrimitive<V, D extends VersionedDoc>(\n config: SpecialisedConfig<V, D>\n): SpecialisedPrimitive<V> {\n if (migrationRegistry.isMarked(config.key, config.primitive)) {\n throw new MigrationError(\n `Cannot construct $${config.primitive}(\"${config.key}\"): this key has been marked as migrated. Migrations are one-way; use the destination primitive instead.`,\n \"already-migrated\",\n config.key,\n config.primitive\n );\n }\n primitiveRegistry.register(config.key, config.primitive, config.callSite);\n\n const inner = signal<V>(config.initialValue);\n let updating = false;\n let currentHandle: DocHandle<D> | undefined;\n\n const loaded = (async () => {\n const handle = await config.getHandle();\n await handle.whenReady();\n currentHandle = handle;\n\n if (config.schemaVersion !== undefined) {\n const targetVersion = config.schemaVersion;\n const migrations = config.migrations ?? {};\n handle.change((doc) => {\n runMigrations(doc as unknown as Record<string, unknown>, targetVersion, migrations);\n setDocVersion(doc as unknown as Record<string, unknown>, targetVersion);\n });\n }\n\n updating = true;\n try {\n inner.value = config.extractValue(handle.doc());\n } finally {\n updating = false;\n }\n\n handle.on(\"change\", (payload) => {\n if (updating) return;\n updating = true;\n try {\n inner.value = config.extractValue(payload.doc);\n } finally {\n updating = false;\n }\n });\n\n effect(() => {\n const value = inner.value;\n if (updating) return;\n if (!currentHandle) return;\n updating = true;\n try {\n currentHandle.change((doc) => {\n config.applyWrite(doc, value);\n });\n } finally {\n updating = false;\n }\n });\n })();\n\n return {\n key: config.key,\n primitive: config.primitive,\n get value() {\n return inner.value;\n },\n set value(next: V) {\n inner.value = next;\n },\n loaded,\n get handle() {\n return currentHandle as unknown as DocHandle<unknown> | undefined;\n },\n };\n}\n\n// ─── $crdtText ──────────────────────────────────────────────────────────────\n\n/** The Automerge document shape backing a $crdtText primitive. */\nexport type TextDoc = VersionedDoc & { text?: string };\n\n/** Options for {@link $crdtText}. */\nexport interface CrdtTextOptions {\n /** Primitive kind label. Defaults to \"peerState\"; Phase 2's $meshState\n * wrapper passes \"meshState\" instead. */\n primitive?: PrimitiveKind;\n /** Async factory that returns a ready DocHandle for the text document. */\n getHandle: () => Promise<DocHandle<TextDoc>>;\n schemaVersion?: number;\n migrations?: Migrations;\n access?: Access;\n callSite?: string;\n}\n\n/**\n * Create a CRDT-backed text primitive. The signal exposes a plain string;\n * writes are diffed into character-level splices via `Automerge.updateText`\n * so that concurrent edits from peers merge cleanly rather than clobbering\n * each other.\n */\nexport function $crdtText(\n key: string,\n initialValue: string,\n options: CrdtTextOptions\n): SpecialisedPrimitive<string> {\n return createSpecialisedPrimitive<string, TextDoc>({\n key,\n primitive: options.primitive ?? \"peerState\",\n initialValue,\n getHandle: options.getHandle,\n extractValue: (doc) => doc.text ?? \"\",\n applyWrite: (doc, value) => {\n if (doc.text === undefined) {\n // First write — seed the field. Subsequent writes will use updateText.\n (doc as unknown as TextDoc).text = value;\n } else {\n updateText(doc, [\"text\"], value);\n }\n },\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n callSite: options.callSite,\n });\n}\n\n// ─── $crdtCounter ───────────────────────────────────────────────────────────\n\n/** The Automerge document shape backing a $crdtCounter primitive. */\nexport type CounterDoc = VersionedDoc & { count?: Counter };\n\n/** Options for {@link $crdtCounter}. */\nexport interface CrdtCounterOptions {\n primitive?: PrimitiveKind;\n getHandle: () => Promise<DocHandle<CounterDoc>>;\n schemaVersion?: number;\n migrations?: Migrations;\n access?: Access;\n callSite?: string;\n}\n\n/**\n * Create a CRDT-backed counter primitive. The signal exposes a plain number;\n * writes compute the delta from the document's current value and call\n * `counter.increment(delta)` on the underlying `Automerge.Counter`. Concurrent\n * increments from peers commute, so two clients each adding 1 to a counter at\n * 5 produce a counter at 7 after merging.\n *\n * Application code that wants to express increments idiomatically can write\n * `counter.value = counter.value + 1`; the signal's reactivity captures the\n * read-then-write pattern and the factory translates it into a proper CRDT\n * increment operation underneath.\n */\nexport function $crdtCounter(\n key: string,\n initialValue: number,\n options: CrdtCounterOptions\n): SpecialisedPrimitive<number> {\n return createSpecialisedPrimitive<number, CounterDoc>({\n key,\n primitive: options.primitive ?? \"peerState\",\n initialValue,\n getHandle: options.getHandle,\n extractValue: (doc) => {\n const c = doc.count;\n if (c === undefined) return 0;\n return c.value;\n },\n applyWrite: (doc, value) => {\n const existing = doc.count;\n if (existing === undefined) {\n (doc as unknown as CounterDoc).count = new Counter(value);\n } else {\n const delta = value - existing.value;\n if (delta !== 0) {\n existing.increment(delta);\n }\n }\n },\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n callSite: options.callSite,\n });\n}\n\n// ─── $crdtList ──────────────────────────────────────────────────────────────\n\n/** The Automerge document shape backing a $crdtList primitive. */\nexport type ListDoc<T> = VersionedDoc & { items?: T[] };\n\n/** Options for {@link $crdtList}. */\nexport interface CrdtListOptions<T> {\n primitive?: PrimitiveKind;\n getHandle: () => Promise<DocHandle<ListDoc<T>>>;\n schemaVersion?: number;\n migrations?: Migrations;\n access?: Access;\n callSite?: string;\n}\n\n/**\n * Create a CRDT-backed list primitive. The signal exposes a plain array;\n * for the Phase 0 cut, writes replace the underlying array wholesale inside\n * an `Automerge.change` block. This is correct for single-writer scenarios\n * and is the simplest way to ship a working list primitive on the same\n * pipeline as text and counter.\n *\n * Phase 1 will replace the write path with proper structural diffing into\n * insert and remove operations so that concurrent edits from peers preserve\n * list ordering. Until then, applications using $crdtList in a multi-writer\n * setting should expect last-writer-wins semantics on the array as a whole.\n */\nexport function $crdtList<T>(\n key: string,\n initialValue: T[],\n options: CrdtListOptions<T>\n): SpecialisedPrimitive<T[]> {\n return createSpecialisedPrimitive<T[], ListDoc<T>>({\n key,\n primitive: options.primitive ?? \"peerState\",\n initialValue,\n getHandle: options.getHandle,\n extractValue: (doc) => (doc.items ? [...doc.items] : []),\n applyWrite: (doc, value) => {\n // Phase 0 naive replacement; see the function-level docstring for the\n // refinement plan.\n (doc as unknown as ListDoc<T>).items = [...value];\n },\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n callSite: options.callSite,\n });\n}\n",
|
|
10
18
|
"/**\n * migrate-primitive — one-way cross-primitive data migration helper.\n *\n * Moving data from one Polly primitive to another ($sharedState to $peerState,\n * $peerState to $meshState, etc.) is a deliberate, one-time, application-authored\n * operation. The three primitive families serialise differently — LWW value plus\n * Lamport clock, plaintext Automerge ops, signed and encrypted Automerge ops —\n * and Polly never silently coerces between them. An application that wants to\n * move data explicitly reads the current value from the source, applies a\n * user-supplied transform, writes the result into the destination, and marks\n * the source as migrated so subsequent reads from it fail loudly rather than\n * returning stale data.\n *\n * This module provides the helper ({@link migratePrimitive}) and the migration\n * registry ({@link migrationRegistry}) that records which (key, primitive kind)\n * pairs have been migrated. Primitive read paths consult the registry at load\n * time and refuse to hydrate sources that have been marked. Migration is one-way\n * by design: there is no rollback, and running a migration twice throws.\n *\n * The registry is in-memory only. Persistence of the migrated flag across page\n * loads is each primitive's own responsibility at its storage boundary; Phase 1\n * and Phase 2 primitives will restore the registry state on startup from their\n * local stores.\n *\n * @example\n * ```ts\n * await migratePrimitive(\n * $sharedState<OldNotes>(\"notes\"),\n * $meshState<NewNotes>(\"notes\", { entries: [] }),\n * (old) => ({ entries: old.entries ?? [] }),\n * );\n * ```\n */\n\nimport type { PrimitiveKind } from \"./primitive-registry\";\n\n/**\n * Minimal interface that every migratable state primitive must satisfy. Real\n * primitive instances will implement this plus everything else their type\n * expects; tests construct plain objects.\n */\nexport interface MigratableState<T> {\n /** Stable logical key the primitive was registered under. */\n readonly key: string;\n /** The primitive kind owning this state. Used to identify entries in the\n * migration registry alongside the key. */\n readonly primitive: PrimitiveKind;\n /** Current value. Must be readable and writable. */\n value: T;\n /** Hydration promise. The migration helper awaits this on both source and\n * destination before reading the source value. */\n readonly loaded: Promise<void>;\n}\n\n/**\n * Error thrown by the migration subsystem. The {@link code} field distinguishes\n * the failure modes so callers can branch on them.\n */\nexport class MigrationError extends Error {\n readonly code: \"already-migrated\" | \"same-primitive-instance\";\n readonly key: string;\n readonly primitive: PrimitiveKind;\n\n constructor(\n message: string,\n code: MigrationError[\"code\"],\n key: string,\n primitive: PrimitiveKind\n ) {\n super(message);\n this.name = \"MigrationError\";\n this.code = code;\n this.key = key;\n this.primitive = primitive;\n }\n}\n\n/**\n * In-memory registry of migrated (key, primitive kind) pairs. Exported as a\n * class so tests can construct fresh instances; application code uses the\n * module-level {@link migrationRegistry} singleton.\n */\nexport class MigrationRegistry {\n private readonly marks = new Set<string>();\n\n private entryKey(key: string, primitive: PrimitiveKind): string {\n return `${primitive}:${key}`;\n }\n\n /** Mark a source primitive as migrated. Idempotent. */\n mark(key: string, primitive: PrimitiveKind): void {\n this.marks.add(this.entryKey(key, primitive));\n }\n\n /** Check whether a source primitive has been marked as migrated. */\n isMarked(key: string, primitive: PrimitiveKind): boolean {\n return this.marks.has(this.entryKey(key, primitive));\n }\n\n /** Drop every mark. Intended for tests; application code should not call this. */\n clear(): void {\n this.marks.clear();\n }\n\n /** Number of recorded marks. Intended for tests. */\n get size(): number {\n return this.marks.size;\n }\n}\n\n/**\n * The process-wide migration registry. Primitives and application migrations\n * both consult it: tests can reset it with {@link MigrationRegistry.clear}.\n */\nexport const migrationRegistry = new MigrationRegistry();\n\n/**\n * Migrate data from one Polly primitive to another. Reads the source's current\n * value, applies the caller's transform, writes the result to the destination,\n * and marks the source as migrated so subsequent reads fail loudly.\n *\n * The helper is one-way and one-time. Running it twice on the same source\n * throws a {@link MigrationError} with code `already-migrated`. Running it\n * with the same object as both source and destination throws with code\n * `same-primitive-instance`.\n *\n * Applications invoke this explicitly on upgrade, once per device, typically\n * inside a startup hook before the primitives that depend on the migrated data\n * are read. It is not a replacement for the schema-version migration protocol\n * inside a single primitive — that handles shape evolution within one primitive\n * family, whereas this helper handles moves between primitive families.\n */\nexport async function migratePrimitive<Source, Destination>(\n source: MigratableState<Source>,\n destination: MigratableState<Destination>,\n transform: (value: Source) => Destination\n): Promise<void> {\n if ((source as unknown as unknown) === (destination as unknown as unknown)) {\n throw new MigrationError(\n `Cannot migrate a primitive to itself: \"${source.key}\" under ${source.primitive}.`,\n \"same-primitive-instance\",\n source.key,\n source.primitive\n );\n }\n if (migrationRegistry.isMarked(source.key, source.primitive)) {\n throw new MigrationError(\n `Cannot migrate: source \"${source.key}\" under $${source.primitive} has already been migrated. Migrations are one-way and one-time.`,\n \"already-migrated\",\n source.key,\n source.primitive\n );\n }\n await source.loaded;\n await destination.loaded;\n const transformed = transform(source.value);\n destination.value = transformed;\n migrationRegistry.mark(source.key, source.primitive);\n}\n",
|
|
11
19
|
"/**\n * PrimitiveRegistry — runtime namespace collision detection across Polly's\n * synced state primitives.\n *\n * The three primitive families ($sharedState, $peerState, $meshState) each store\n * data under a developer-chosen logical key. If two different primitives both\n * claim the same key, the developer almost certainly has a bug: the on-disk\n * formats are incompatible, no sync happens between them, and whichever primitive\n * resolves first silently \"wins\" from the developer's perspective. By the time\n * the mistake is noticed, data has diverged.\n *\n * The registry catches the mistake at the first mismatched registration and\n * throws a structured error naming the key, both primitives, and (when available)\n * the call site of each registration. This is run-to-failure by design: a\n * collision is always a bug, and the failure should be loud.\n *\n * Same primitive re-registering the same key is allowed and is a no-op — it\n * supports hot module reloading and component re-mounts without spurious errors.\n * Changing the primitive kind of an existing key is still an error; developers\n * doing that during local HMR should hard-reload to reset the registry.\n *\n * @example\n * ```ts\n * primitiveRegistry.register(\"notes\", \"sharedState\", \"src/app.ts:10\");\n * primitiveRegistry.register(\"notes\", \"peerState\", \"src/other.ts:22\");\n * // throws PrimitiveCollisionError — names both primitives and both call sites\n * ```\n */\n\n/**\n * Canonical identifiers for Polly's synced state primitives. The registry\n * uses these as opaque labels; nothing else in Polly needs to match them.\n */\nexport type PrimitiveKind =\n | \"sharedState\"\n | \"syncedState\"\n | \"persistedState\"\n | \"state\"\n | \"peerState\"\n | \"meshState\";\n\n/**\n * Thrown when a logical key is registered under more than one primitive.\n * The message names the key, both primitives, and (when available) the\n * call site of each registration, so the developer can navigate to both\n * sites from the error output.\n */\nexport class PrimitiveCollisionError extends Error {\n readonly key: string;\n readonly firstPrimitive: PrimitiveKind;\n readonly firstCallSite: string | undefined;\n readonly secondPrimitive: PrimitiveKind;\n readonly secondCallSite: string | undefined;\n\n constructor(\n key: string,\n firstPrimitive: PrimitiveKind,\n firstCallSite: string | undefined,\n secondPrimitive: PrimitiveKind,\n secondCallSite: string | undefined\n ) {\n const firstLocation = firstCallSite ? ` (at ${firstCallSite})` : \"\";\n const secondLocation = secondCallSite ? ` (at ${secondCallSite})` : \"\";\n super(\n `Polly primitive key collision: \"${key}\" is already registered as ` +\n `$${firstPrimitive}${firstLocation} and cannot also be registered ` +\n `as $${secondPrimitive}${secondLocation}. Pick a different key or ` +\n `use the same primitive in both places.`\n );\n this.name = \"PrimitiveCollisionError\";\n this.key = key;\n this.firstPrimitive = firstPrimitive;\n this.firstCallSite = firstCallSite;\n this.secondPrimitive = secondPrimitive;\n this.secondCallSite = secondCallSite;\n }\n}\n\ntype RegistryEntry = {\n primitive: PrimitiveKind;\n callSite: string | undefined;\n};\n\n/**\n * A small Map-backed registry of \"logical key → primitive kind\". Exported as\n * a class so tests can construct fresh instances without sharing state; the\n * module-level {@link primitiveRegistry} singleton is what application code\n * actually uses.\n */\nexport class PrimitiveRegistry {\n private readonly entries = new Map<string, RegistryEntry>();\n\n /**\n * Register a key under a primitive kind. Re-registering the same key under\n * the same primitive is a no-op, which is what hot module reloading and\n * component re-mounts produce.\n *\n * @throws {PrimitiveCollisionError} if the key is already registered under\n * a different primitive kind.\n */\n register(key: string, primitive: PrimitiveKind, callSite?: string): void {\n const existing = this.entries.get(key);\n if (existing && existing.primitive !== primitive) {\n throw new PrimitiveCollisionError(\n key,\n existing.primitive,\n existing.callSite,\n primitive,\n callSite\n );\n }\n if (!existing) {\n this.entries.set(key, { primitive, callSite });\n }\n }\n\n /**\n * True if the key has been registered (under any primitive kind).\n */\n has(key: string): boolean {\n return this.entries.has(key);\n }\n\n /**\n * Look up the primitive kind a key is registered under, if any.\n * Returns undefined for unregistered keys.\n */\n kindOf(key: string): PrimitiveKind | undefined {\n return this.entries.get(key)?.primitive;\n }\n\n /**\n * Drop every registration. Intended for test setup and teardown; application\n * code should not call this.\n */\n clear(): void {\n this.entries.clear();\n }\n\n /**\n * Number of registered keys. Intended for tests.\n */\n get size(): number {\n return this.entries.size;\n }\n}\n\n/**\n * The process-wide primitive registry. Application code registers here\n * implicitly via primitive constructors; tests can reset it with `clear()`.\n */\nexport const primitiveRegistry = new PrimitiveRegistry();\n",
|
|
12
20
|
"/**\n * schema-version — plumbing for the shared schema-version migration protocol\n * used by $peerState and $meshState documents.\n *\n * Every peer-first document carries a reserved schema-version field. When a\n * client loads a document whose stored version is lower than the application's\n * declared target version, Polly walks the registered migrations in sequence,\n * mutating the document from one version to the next, and stamps the new\n * version on the way through. When the stored version is higher, Polly refuses\n * to load and surfaces a structured error: the application is older than the\n * document and should be upgraded.\n *\n * For concurrent writes across mixed client versions, every op also carries\n * the schema version under which it was produced. Ops whose version is lower\n * than the document's current version are rejected at sync time; the peer\n * producing them needs to upgrade its local replica through the migration\n * runner before retrying. Ops whose version is higher mean the current peer\n * is behind the application that produced them; rejecting them prevents\n * corruption while still surfacing the mismatch to the user.\n *\n * This module provides only the plumbing — the reserved field name, the\n * reader and writer for the version field, the migration runner, and the\n * op-version compatibility check. Phase 1 and Phase 2 primitives will consume\n * these helpers; nothing in this file depends on Automerge or any transport.\n */\n\n/**\n * The reserved field name used to store the schema version on every peer-first\n * document. Applications must not use this field for their own data; the\n * primitive constructors reserve it at the document boundary.\n */\nexport const SCHEMA_VERSION_FIELD = \"__schemaVersion\" as const;\n\n/**\n * The minimal shape every peer-first document satisfies. Applications layer\n * their own fields on top of this; the reserved {@link SCHEMA_VERSION_FIELD}\n * is the only required key.\n */\nexport type VersionedDoc = {\n [SCHEMA_VERSION_FIELD]?: number;\n [otherField: string]: unknown;\n};\n\n/**\n * A single migration step. Mutates the document in place, transforming it\n * from version (target - 1) to version target. The implementation must be\n * total (accept any valid v(target-1) document) and deterministic (running\n * it twice produces the same result). Applications that want return-a-new-doc\n * semantics should copy inside the migration body rather than returning.\n */\nexport type Migration = (doc: Record<string, unknown>) => void;\n\n/**\n * A map from target schema version to the migration that produces it. The\n * keys must be contiguous from 1 upward through the application's target\n * version, with no gaps. Gaps throw at migration time.\n *\n * @example\n * ```ts\n * const migrations: Migrations = {\n * 1: (doc) => { doc[\"title\"] = \"\"; }, // v0 → v1\n * 2: (doc) => { doc[\"tags\"] = []; }, // v1 → v2\n * 3: (doc) => { doc[\"archived\"] = false; delete doc[\"deleted\"]; }, // v2 → v3\n * };\n * ```\n */\nexport type Migrations = Record<number, Migration>;\n\n/**\n * Error thrown by the schema-version subsystem. The `code` field distinguishes\n * the failure modes so that callers (primitive constructors, application error\n * handlers) can decide whether to surface the error to the user, prompt an\n * upgrade, or drop the offending op.\n */\nexport class SchemaVersionError extends Error {\n readonly code:\n | \"doc-ahead-of-app\"\n | \"missing-migration\"\n | \"op-older-than-doc\"\n | \"op-newer-than-doc\";\n readonly docVersion?: number;\n readonly targetVersion?: number;\n readonly opVersion?: number;\n readonly missingVersion?: number;\n\n constructor(\n message: string,\n code: SchemaVersionError[\"code\"],\n details: {\n docVersion?: number;\n targetVersion?: number;\n opVersion?: number;\n missingVersion?: number;\n } = {}\n ) {\n super(message);\n this.name = \"SchemaVersionError\";\n this.code = code;\n if (details.docVersion !== undefined) this.docVersion = details.docVersion;\n if (details.targetVersion !== undefined) this.targetVersion = details.targetVersion;\n if (details.opVersion !== undefined) this.opVersion = details.opVersion;\n if (details.missingVersion !== undefined) this.missingVersion = details.missingVersion;\n }\n}\n\n/**\n * Read the schema version stored on a document. Returns 0 for documents that\n * have never been stamped (undefined field), which is the canonical \"pre-v1\"\n * sentinel — the first migration in a registry is always keyed at 1 and\n * handles the undefined-to-1 transition.\n */\nexport function getDocVersion(doc: unknown): number {\n if (typeof doc !== \"object\" || doc === null) return 0;\n const record = doc as unknown as Record<string, unknown>;\n const value = record[SCHEMA_VERSION_FIELD];\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0 ? value : 0;\n}\n\n/**\n * Stamp a schema version onto a document in place. Primitive constructors call\n * this after each migration step and once on document creation.\n */\nexport function setDocVersion(doc: Record<string, unknown>, version: number): void {\n doc[SCHEMA_VERSION_FIELD] = version;\n}\n\n/**\n * Run any pending migrations on a document. Mutates the document in place.\n *\n * @throws {SchemaVersionError} with code `doc-ahead-of-app` if the document is\n * already at a version higher than the application's target.\n * @throws {SchemaVersionError} with code `missing-migration` if an intermediate\n * migration is missing from the registry.\n */\nexport function runMigrations(\n doc: Record<string, unknown>,\n targetVersion: number,\n migrations: Migrations\n): void {\n const current = getDocVersion(doc);\n if (current > targetVersion) {\n throw new SchemaVersionError(\n `Document is at schema version ${current} but the application targets ${targetVersion}. Upgrade the application to continue.`,\n \"doc-ahead-of-app\",\n { docVersion: current, targetVersion }\n );\n }\n for (let v = current + 1; v <= targetVersion; v++) {\n const migration = migrations[v];\n if (!migration) {\n throw new SchemaVersionError(\n `Missing migration for schema version ${v}. Migrations must be contiguous from ${current + 1} through ${targetVersion}.`,\n \"missing-migration\",\n { docVersion: current, targetVersion, missingVersion: v }\n );\n }\n migration(doc);\n setDocVersion(doc, v);\n }\n}\n\n/**\n * Result of an op-version compatibility check. Discriminated by the {@link compatible}\n * field so callers can switch cleanly.\n */\nexport type OpVersionCheck =\n | { compatible: true }\n | {\n compatible: false;\n reason: \"op-older-than-doc\" | \"op-newer-than-doc\";\n opVersion: number;\n docVersion: number;\n };\n\n/**\n * Check whether an incoming op's declared schema version is compatible with\n * the document's current version. Ops that match are applied; ops that are\n * older are rejected (the producing peer is behind and should migrate); ops\n * that are newer are also rejected (the current peer is behind and should\n * upgrade).\n */\nexport function checkOpVersion(opVersion: number, docVersion: number): OpVersionCheck {\n if (opVersion < docVersion) {\n return { compatible: false, reason: \"op-older-than-doc\", opVersion, docVersion };\n }\n if (opVersion > docVersion) {\n return { compatible: false, reason: \"op-newer-than-doc\", opVersion, docVersion };\n }\n return { compatible: true };\n}\n\n/**\n * Convenience wrapper around {@link checkOpVersion} that throws a structured\n * {@link SchemaVersionError} on incompatibility. Useful inside sync handlers\n * that want to surface the mismatch through their normal error pipeline.\n */\nexport function assertOpVersion(opVersion: number, docVersion: number): void {\n const result = checkOpVersion(opVersion, docVersion);\n if (result.compatible) return;\n const message =\n result.reason === \"op-older-than-doc\"\n ? `Incoming op was produced at schema version ${opVersion} but the document is at version ${docVersion}. The producing peer is behind.`\n : `Incoming op was produced at schema version ${opVersion} but the document is at version ${docVersion}. The current peer is behind and should upgrade.`;\n throw new SchemaVersionError(message, result.reason, { opVersion, docVersion });\n}\n",
|
|
13
|
-
"/**\n * crdt-state — base machinery for Polly's peer-first state primitives.\n *\n * This module is transport-agnostic: it takes a caller-supplied async factory\n * that produces a ready {@link DocHandle}, binds it bidirectionally to a\n * Preact signal, runs any pending schema migrations on load, and integrates\n * with the primitive-registry and migration-registry guards. Phase 1's\n * $peerState and Phase 2's $meshState both construct these base primitives\n * with their own handle factories — one over Automerge-Repo's WebSocket\n * client adapter, the other over WebRTC — and the base never knows which.\n *\n * The signal-to-handle binding uses an `updating` guard flag to prevent write\n * loops: when a local signal assignment runs the effect that pushes the value\n * into `handle.change`, the flag is raised so that the 'change' event the\n * handle fires back is ignored. The same flag protects in the other direction\n * when a remote change seeds the signal.\n *\n * For the Phase 0 cut, writes are applied with a naive top-level structural\n * replacement inside the `Automerge.change` block. This is correct for\n * JSON-shaped documents with scalar and flat-object fields and is good enough\n * to exercise the rest of the pipeline. The specialised variants for text,\n * counters, and lists (which require type-specific operation capture to\n * preserve concurrent-edit semantics) land in Phase 1's crdt-specialised.ts.\n */\n\nimport type { DocHandle } from \"@automerge/automerge-repo\";\nimport { effect, signal } from \"@preact/signals\";\nimport type { Access } from \"./access\";\nimport { type MigratableState, MigrationError, migrationRegistry } from \"./migrate-primitive\";\nimport { type PrimitiveKind, primitiveRegistry } from \"./primitive-registry\";\nimport {\n type Migrations,\n runMigrations,\n SCHEMA_VERSION_FIELD,\n setDocVersion,\n type VersionedDoc,\n} from \"./schema-version\";\n\n/**\n * The interface a Polly peer-first primitive exposes at the call site. It\n * satisfies {@link MigratableState} so that the cross-primitive migration\n * helper can consume it directly.\n */\nexport interface CrdtPrimitive<T extends VersionedDoc> extends MigratableState<T> {\n /** Stable logical key the primitive was registered under. */\n readonly key: string;\n /** Primitive kind — one of the {@link PrimitiveKind} labels. */\n readonly primitive: PrimitiveKind;\n /** Current value. Writes push into the backing Automerge document. */\n value: T;\n /** Resolves when the handle is ready and migrations have run. */\n readonly loaded: Promise<void>;\n /** The underlying {@link DocHandle}, populated after {@link loaded} resolves.\n * Intended for advanced escape hatches; most callers should stay at the\n * signal surface. */\n readonly handle: DocHandle<T> | undefined;\n}\n\n/**\n * Options for constructing a base CRDT-backed primitive. Phase 1 and Phase 2\n * primitive constructors pass a transport-specific {@link getHandle} factory\n * and their own {@link primitive} label; everything else is shared.\n */\nexport interface CrdtStateOptions<T extends VersionedDoc> {\n /** Stable logical key identifying this piece of state. */\n key: string;\n /** Primitive kind label for registry and error-message purposes. */\n primitive: PrimitiveKind;\n /** Initial value if no stored document exists yet. Applied by the caller's\n * handle factory; the base module does not create documents itself. */\n initialValue: T;\n /** Async factory that resolves to a ready {@link DocHandle}. The factory is\n * responsible for repo lookup, document creation, and any transport-specific\n * setup. The base module calls this once, during hydration. */\n getHandle: () => Promise<DocHandle<T>>;\n /** Target schema version for the application. If set, migrations run on\n * load to bring the document up to this version before the signal hydrates. */\n schemaVersion?: number;\n /** Migration table. Ignored if {@link schemaVersion} is not set. */\n migrations?: Migrations;\n /** Declarative access predicates. Not consumed by the base module; the\n * transport-specific constructors compile it to their enforcement layer. */\n access?: Access;\n /** Optional free-text call-site label for primitive-registry error messages. */\n callSite?: string;\n}\n\n/**\n * Construct a base CRDT-backed Polly primitive. Integrates with\n * primitive-registry (for collision detection), migration-registry (for\n * cross-family migration guards), and schema-version (for on-load migrations).\n *\n * @throws {MigrationError} if the source key has been marked as migrated.\n * @throws {PrimitiveCollisionError} if the key is already registered under a\n * different primitive kind.\n */\nexport function $crdtState<T extends VersionedDoc>(options: CrdtStateOptions<T>): CrdtPrimitive<T> {\n if (migrationRegistry.isMarked(options.key, options.primitive)) {\n throw new MigrationError(\n `Cannot construct $${options.primitive}(\"${options.key}\"): this key has been marked as migrated. Migrations are one-way; use the destination primitive instead.`,\n \"already-migrated\",\n options.key,\n options.primitive\n );\n }\n primitiveRegistry.register(options.key, options.primitive, options.callSite);\n\n const inner = signal<T>(options.initialValue);\n let updating = false;\n let currentHandle: DocHandle<T> | undefined;\n\n const loaded = (async () => {\n const handle = await options.getHandle();\n await handle.whenReady();\n currentHandle = handle;\n\n // Run any pending schema migrations inside a change block so they land\n // as a single Automerge operation set.\n if (options.schemaVersion !== undefined) {\n const targetVersion = options.schemaVersion;\n const migrations = options.migrations ?? {};\n handle.change((doc) => {\n runMigrations(doc as unknown as Record<string, unknown>, targetVersion, migrations);\n // runMigrations stamps the version on every intermediate step; make\n // sure the final value is recorded even when no migrations ran.\n setDocVersion(doc as unknown as Record<string, unknown>, targetVersion);\n });\n }\n\n // Seed the signal with the hydrated doc state. Raise the guard first so\n // the 'change' listener we install below does not echo this write back.\n updating = true;\n try {\n inner.value = cloneDoc(handle.doc());\n } finally {\n updating = false;\n }\n\n // Remote-changes-to-signal binding.\n handle.on(\"change\", (payload) => {\n if (updating) return;\n updating = true;\n try {\n inner.value = cloneDoc(payload.doc);\n } finally {\n updating = false;\n }\n });\n\n // Signal-to-remote binding. The effect runs once on registration with\n // the already-hydrated value; the guard makes that first run a no-op at\n // the handle level because updating is false but the doc already equals\n // the signal, so Automerge records no new operations.\n effect(() => {\n const value = inner.value;\n if (updating) return;\n if (!currentHandle) return;\n updating = true;\n try {\n currentHandle.change((doc) => {\n applyTopLevel(doc as unknown as Record<string, unknown>, value);\n });\n } finally {\n updating = false;\n }\n });\n })();\n\n return {\n key: options.key,\n primitive: options.primitive,\n get value() {\n return inner.value;\n },\n set value(next: T) {\n inner.value = next;\n },\n loaded,\n get handle() {\n return currentHandle;\n },\n };\n}\n\n/**\n * Shallow clone of an Automerge doc into a plain JS object. Automerge docs\n * are Proxies; the signal holds a detached plain-object snapshot so that\n * application code does not accidentally mutate the CRDT through the signal.\n */\nfunction cloneDoc<T>(doc: T): T {\n return JSON.parse(JSON.stringify(doc)) as T;\n}\n\n/**\n * Copy every top-level field from the incoming value onto the Automerge doc.\n * This is the naive Phase 0 write path: correct for flat JSON-shaped documents\n * and good enough for the base module's tests. Specialised primitives in\n * Phase 1 will replace this with type-aware operation capture for text,\n * counters, and lists.\n *\n * The reserved schema-version field is not copied — it is managed by the\n * migration subsystem and must not be overwritten by application writes.\n */\nfunction applyTopLevel<T extends VersionedDoc>(doc: Record<string, unknown>, value: T): void {\n for (const key of Object.keys(value)) {\n if (key === SCHEMA_VERSION_FIELD) continue;\n doc[key] = (value as unknown as Record<string, unknown>)[key];\n }\n}\n",
|
|
14
|
-
"/**\n * mesh-state — Phase 2 wrappers exposing $meshState, $meshText, $meshCounter,\n * and $meshList. These are the application-facing constructors for the\n * strongest resilience tier in RFC-041: every device is a full Automerge\n * replica, the server is *not on the data path at all*, and the application\n * functions with zero server uptime once direct peer connections are\n * established.\n *\n * Each primitive wraps the corresponding Phase 0 base ($crdtState, $crdtText,\n * $crdtCounter, $crdtList) with three additions:\n *\n * 1. The `primitive` label is hard-coded to \"meshState\" so the\n * primitive-registry collision detection knows which family the key\n * belongs to.\n *\n * 2. A handle factory that resolves the application's logical key to an\n * Automerge DocumentId via a per-Repo key map, identical in shape to\n * the $peerState factory but registered against a separate Repo\n * configured for the mesh transport (signed and encrypted at the\n * network layer).\n *\n * 3. Signing and encryption are mandatory, not optional. Where $peerState\n * accepts encrypt/sign as opt-in flags (currently throwing in Phase 1),\n * $meshState requires every operation to be signed by the originating\n * peer's key and encrypted under the document's symmetric key. The\n * mechanism lives in the wrapping MeshNetworkAdapter that the Repo\n * uses for transport.\n *\n * The Repo itself is supplied by the application via {@link configureMeshState}\n * or per-call via the `repo` option. In Phase 2 the production transport will\n * be a WebRTC mesh adapter wrapping signing+encryption around an in-process\n * RTCDataChannel; for tests and for the early Phase 2 cut, an in-memory\n * loopback adapter pair satisfies the same contract.\n */\n\nimport type { DocHandle, DocumentId, Repo } from \"@automerge/automerge-repo\";\nimport type { Access } from \"./access\";\nimport {\n $crdtCounter,\n $crdtList,\n $crdtText,\n type CounterDoc,\n type ListDoc,\n type SpecialisedPrimitive,\n type TextDoc,\n} from \"./crdt-specialised\";\nimport { $crdtState, type CrdtPrimitive } from \"./crdt-state\";\nimport type { Migrations, VersionedDoc } from \"./schema-version\";\n\n/** Common option shape across all four $mesh* primitives. */\nexport interface MeshStateOptions {\n /** Override the default Repo for this primitive. The Repo must be\n * configured with the mesh transport (signing and encryption at the\n * network layer). */\n repo?: Repo;\n /** Schema version target for the application. Migrations run on load. */\n schemaVersion?: number;\n /** Migration table keyed by target version. Required if schemaVersion is set. */\n migrations?: Migrations;\n /** Declarative read/write access. The mesh transport compiles this into\n * a public-key set used by the signing layer to verify incoming ops. */\n access?: Access;\n}\n\n/** Internal: per-Repo key → DocumentId map. */\nconst keyMapsByRepo = new WeakMap<Repo, Map<string, DocumentId>>();\nlet defaultRepo: Repo | undefined;\n\n/**\n * Set the default Repo that the $mesh* primitives use when no `repo` option\n * is supplied. Calling this with a new Repo clears the per-Repo key map so\n * that tests start each scenario with a fresh document space.\n *\n * Production code typically calls this once at application startup with a\n * Repo configured for the mesh transport. Tests call it before each\n * scenario with an in-memory or loopback Repo.\n */\nexport function configureMeshState(repo: Repo): void {\n defaultRepo = repo;\n keyMapsByRepo.set(repo, new Map());\n}\n\n/**\n * Reset the mesh-state subsystem to its initial unconfigured state.\n * Intended for tests; production code should not call this.\n */\nexport function resetMeshState(): void {\n defaultRepo = undefined;\n}\n\nfunction resolveRepo(option: Repo | undefined): Repo {\n const repo = option ?? defaultRepo;\n if (!repo) {\n throw new Error(\n \"Polly $meshState: no Repo configured. Call configureMeshState(repo) at startup or pass { repo } in the primitive options.\"\n );\n }\n return repo;\n}\n\nfunction getKeyMap(repo: Repo): Map<string, DocumentId> {\n let map = keyMapsByRepo.get(repo);\n if (!map) {\n map = new Map();\n keyMapsByRepo.set(repo, map);\n }\n return map;\n}\n\nfunction buildHandleFactory<D>(\n repo: Repo,\n key: string,\n initialDoc: D\n): () => Promise<DocHandle<D>> {\n return async () => {\n const map = getKeyMap(repo);\n const existingId = map.get(key);\n if (existingId !== undefined) {\n return repo.find<D>(existingId);\n }\n const handle = repo.create<D>(initialDoc);\n map.set(key, handle.documentId);\n return handle;\n };\n}\n\n// ─── $meshState ─────────────────────────────────────────────────────────────\n\n/**\n * Create a peer-replicated state primitive backed by Automerge with a mesh\n * transport. Every device holds a full replica; no central server holds a\n * replica. Operations flow peer-to-peer through signed and encrypted\n * messages on the underlying transport.\n *\n * @example\n * ```ts\n * const journal = $meshState<Journal>(\"journal\", { entries: [] });\n * await journal.loaded;\n * journal.value = { entries: [\"my private thoughts\"] };\n * ```\n */\nexport function $meshState<T extends VersionedDoc>(\n key: string,\n initialValue: T,\n options: MeshStateOptions = {}\n): CrdtPrimitive<T> {\n const repo = resolveRepo(options.repo);\n return $crdtState<T>({\n key,\n primitive: \"meshState\",\n initialValue,\n getHandle: buildHandleFactory<T>(repo, key, initialValue),\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n });\n}\n\n// ─── $meshText ──────────────────────────────────────────────────────────────\n\n/**\n * Create a peer-replicated text primitive backed by a mesh transport.\n * Concurrent character-level edits from peers merge cleanly via Automerge's\n * updateText splicing, and every operation is signed and encrypted before\n * leaving the originating peer.\n */\nexport function $meshText(\n key: string,\n initialValue: string,\n options: MeshStateOptions = {}\n): SpecialisedPrimitive<string> {\n const repo = resolveRepo(options.repo);\n return $crdtText(key, initialValue, {\n primitive: \"meshState\",\n getHandle: buildHandleFactory<TextDoc>(repo, key, { text: initialValue }),\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n });\n}\n\n// ─── $meshCounter ───────────────────────────────────────────────────────────\n\n/**\n * Create a peer-replicated counter primitive backed by a mesh transport.\n * Concurrent increments commute, and every operation is signed and\n * encrypted before leaving the originating peer.\n */\nexport function $meshCounter(\n key: string,\n initialValue: number,\n options: MeshStateOptions = {}\n): SpecialisedPrimitive<number> {\n const repo = resolveRepo(options.repo);\n return $crdtCounter(key, initialValue, {\n primitive: \"meshState\",\n getHandle: buildHandleFactory<CounterDoc>(repo, key, {}),\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n });\n}\n\n// ─── $meshList ──────────────────────────────────────────────────────────────\n\n/**\n * Create a peer-replicated list primitive backed by a mesh transport.\n * Phase 0 naive replacement applies to writes for now; Phase 1.1 will\n * refine with structural diff-to-splice for concurrent insert/remove\n * preservation.\n */\nexport function $meshList<T>(\n key: string,\n initialValue: T[],\n options: MeshStateOptions = {}\n): SpecialisedPrimitive<T[]> {\n const repo = resolveRepo(options.repo);\n return $crdtList<T>(key, initialValue, {\n primitive: \"meshState\",\n getHandle: buildHandleFactory<ListDoc<T>>(repo, key, { items: initialValue }),\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n });\n}\n",
|
|
15
|
-
"/**\n * mesh-webrtc-adapter — Phase 2 browser-side WebRTC transport for Polly's\n * $meshState primitive. Extends Automerge's NetworkAdapter base class and\n * uses real native RTCPeerConnection / RTCDataChannel instances to carry\n * sync messages between peers.\n *\n * This is the \"base\" transport that MeshNetworkAdapter wraps with its\n * sign-then-encrypt envelope. The stack is:\n *\n * $meshState\n * └─ Repo\n * └─ MeshNetworkAdapter (sign + encrypt)\n * └─ MeshWebRTCAdapter (real data channels)\n * └─ MeshSignalingClient (SDP/ICE relay)\n * └─ signalingServer (Elysia plugin on the host app)\n *\n * Because WebRTC lives in browsers, this module is browser-only. It\n * assumes global RTCPeerConnection, RTCDataChannel, and WebSocket types\n * are available. Under bun:test the classes cannot be exercised\n * end-to-end — the first validation path is either Playwright running a\n * real browser, a Puppeteer harness, or a human testing a browser-side\n * example app that consumes the adapter.\n *\n * What this module does at runtime:\n *\n * - Constructs a MeshWebRTCAdapter with a signalling client and a local\n * peer id. No data channels exist at construction time.\n *\n * - When Automerge's NetworkSubsystem calls connect(peerId) on the\n * adapter, it starts listening for signals from remote peers and is\n * ready to build peer connections as they are discovered.\n *\n * - When a remote peer sends an SDP offer via the signalling channel,\n * the adapter builds a fresh RTCPeerConnection, accepts the offer,\n * produces an answer, sends it back through signalling, and wires the\n * received data channel to emit Automerge Message events upward.\n *\n * - When the local repo calls send(message), the adapter looks up the\n * peer connection for message.targetId and writes the serialised\n * bytes to its data channel. If no connection exists yet, the adapter\n * creates one by sending an SDP offer through signalling. Outgoing\n * messages are queued until the channel is open.\n *\n * - Disconnect tears down every peer connection and closes the\n * signalling client.\n */\n\nimport {\n type Message,\n NetworkAdapter,\n type PeerId,\n type PeerMetadata,\n} from \"@automerge/automerge-repo\";\nimport type { MeshSignalingClient } from \"./mesh-signaling-client\";\n\n/** Standard STUN servers for NAT traversal. In production, callers who\n * need TURN fallback for peers behind symmetric NATs should replace this\n * with their own ICE server list. */\nexport const DEFAULT_ICE_SERVERS: RTCIceServer[] = [\n { urls: \"stun:stun.l.google.com:19302\" },\n { urls: \"stun:stun1.l.google.com:19302\" },\n];\n\n/** Options for constructing a {@link MeshWebRTCAdapter}. */\nexport interface MeshWebRTCAdapterOptions {\n /** The signalling client the adapter uses to exchange SDP and ICE\n * candidates with other peers. Typically constructed once per\n * application and shared across any adapters that need it. */\n signaling: MeshSignalingClient;\n /** The local peer id. Must match the peer id the signalling client\n * registered with. */\n peerId: string;\n /** Peer ids to connect to on startup. When `connect()` is called, the\n * adapter iterates this list and initiates a WebRTC connection to each\n * one by sending an SDP offer through the signalling channel. Peers\n * not in this list can still connect by sending an offer *to* this\n * adapter. The natural source for this list is the keyring's\n * knownPeers map keys. */\n knownPeerIds?: string[];\n /** Optional ICE server list override. Defaults to {@link DEFAULT_ICE_SERVERS}. */\n iceServers?: RTCIceServer[];\n /** Optional data channel label. Defaults to \"polly-mesh\". Applications\n * that share a signalling server between multiple meshes may want\n * distinct labels per mesh. */\n dataChannelLabel?: string;\n}\n\n/** Types of signalling payload this adapter exchanges through the\n * signalling channel. The signalling server treats these as opaque. */\ntype SignalingPayload =\n | { kind: \"offer\"; sdp: RTCSessionDescriptionInit }\n | { kind: \"answer\"; sdp: RTCSessionDescriptionInit }\n | { kind: \"ice\"; candidate: RTCIceCandidateInit };\n\n/** State tracked for each remote peer. */\ninterface PeerSlot {\n connection: RTCPeerConnection;\n channel: RTCDataChannel | undefined;\n /** Outgoing messages queued while the channel is still connecting.\n * Typed as ArrayBuffer-backed so they are directly usable by\n * RTCDataChannel.send under TypeScript's strict buffer-source typing. */\n pendingSends: Uint8Array<ArrayBuffer>[];\n}\n\n/**\n * Automerge-Repo NetworkAdapter backed by real WebRTC data channels.\n * Manages one RTCPeerConnection per remote peer and uses a supplied\n * {@link MeshSignalingClient} for SDP/ICE exchange.\n */\nexport class MeshWebRTCAdapter extends NetworkAdapter {\n readonly signaling: MeshSignalingClient;\n readonly iceServers: RTCIceServer[];\n readonly dataChannelLabel: string;\n readonly knownPeerIds: string[];\n private readonly slots = new Map<string, PeerSlot>();\n private ready = false;\n private readyResolver: (() => void) | undefined;\n\n constructor(options: MeshWebRTCAdapterOptions) {\n super();\n this.signaling = options.signaling;\n this.iceServers = options.iceServers ?? DEFAULT_ICE_SERVERS;\n this.dataChannelLabel = options.dataChannelLabel ?? \"polly-mesh\";\n this.knownPeerIds = options.knownPeerIds ?? [];\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n whenReady(): Promise<void> {\n if (this.ready) return Promise.resolve();\n return new Promise((resolve) => {\n this.readyResolver = resolve;\n });\n }\n\n /**\n * Start the adapter. Wires the signalling client's onSignal callback\n * to the adapter's dispatch, opens the signalling connection if it\n * is not already open, and marks the adapter ready.\n */\n connect(peerId: PeerId, peerMetadata?: PeerMetadata): void {\n this.peerId = peerId;\n if (peerMetadata !== undefined) {\n this.peerMetadata = peerMetadata;\n }\n this.ready = true;\n this.readyResolver?.();\n\n // Initiate WebRTC connections to every known peer. This is the\n // discovery step: once the SDP exchange completes and the data\n // channel opens, the adapter emits 'peer-candidate' and the Repo's\n // NetworkSubsystem learns about the peer.\n for (const remotePeerId of this.knownPeerIds) {\n if (remotePeerId !== (peerId as unknown as string) && !this.slots.has(remotePeerId)) {\n this.createInitiatingSlot(remotePeerId);\n }\n }\n }\n\n disconnect(): void {\n for (const slot of this.slots.values()) {\n slot.channel?.close();\n slot.connection.close();\n }\n this.slots.clear();\n this.signaling.close();\n this.ready = false;\n this.emit(\"close\");\n }\n\n /**\n * Send a sync message to a specific remote peer. If no RTCPeerConnection\n * exists yet, the adapter initiates one by producing an SDP offer and\n * sending it through the signalling channel; the outgoing bytes are\n * queued until the data channel is open.\n */\n send(message: Message): void {\n const targetId = message.targetId as unknown as string;\n const bytes = this.serialiseMessage(message);\n let slot = this.slots.get(targetId);\n if (!slot) {\n slot = this.createInitiatingSlot(targetId);\n }\n if (slot.channel && slot.channel.readyState === \"open\") {\n slot.channel.send(bytes);\n } else {\n slot.pendingSends.push(bytes);\n }\n }\n\n /**\n * Entry point the signalling client calls when it receives a signal\n * from a remote peer. Dispatches on the payload `kind` to either\n * accept an offer (building an answer), apply an answer, or add an\n * ICE candidate. Exposed publicly so the caller that constructs the\n * signalling client can wire the onSignal callback directly to this\n * method.\n */\n handleSignal(fromPeerId: string, rawPayload: unknown): void {\n const payload = rawPayload as unknown as SignalingPayload;\n if (!payload || typeof payload !== \"object\" || !(\"kind\" in payload)) {\n return;\n }\n switch (payload.kind) {\n case \"offer\":\n void this.handleOffer(fromPeerId, payload.sdp);\n return;\n case \"answer\":\n void this.handleAnswer(fromPeerId, payload.sdp);\n return;\n case \"ice\":\n void this.handleIceCandidate(fromPeerId, payload.candidate);\n return;\n }\n }\n\n private createInitiatingSlot(targetId: string): PeerSlot {\n const connection = new RTCPeerConnection({ iceServers: this.iceServers });\n const channel = connection.createDataChannel(this.dataChannelLabel, { ordered: true });\n const slot: PeerSlot = { connection, channel, pendingSends: [] };\n this.slots.set(targetId, slot);\n this.wireConnection(targetId, connection);\n this.wireDataChannel(targetId, channel);\n void this.initiateOffer(targetId, connection);\n return slot;\n }\n\n private async initiateOffer(targetId: string, connection: RTCPeerConnection): Promise<void> {\n const offer = await connection.createOffer();\n await connection.setLocalDescription(offer);\n this.signaling.sendSignal(targetId, { kind: \"offer\", sdp: offer } satisfies SignalingPayload);\n }\n\n private async handleOffer(fromPeerId: string, sdp: RTCSessionDescriptionInit): Promise<void> {\n const existing = this.slots.get(fromPeerId);\n if (existing) {\n // Glare: we already initiated a connection to this peer. Resolve by\n // peer-id ordering: the lexicographically lower id yields its own\n // offer and accepts the incoming one. The higher id ignores the\n // incoming offer and waits for the answer to its own.\n const localId = this.peerId as unknown as string;\n if (localId > fromPeerId) {\n return;\n }\n existing.channel?.close();\n existing.connection.close();\n this.slots.delete(fromPeerId);\n }\n\n const connection = new RTCPeerConnection({ iceServers: this.iceServers });\n const slot: PeerSlot = { connection, channel: undefined, pendingSends: [] };\n this.slots.set(fromPeerId, slot);\n this.wireConnection(fromPeerId, connection);\n connection.ondatachannel = (event) => {\n slot.channel = event.channel;\n this.wireDataChannel(fromPeerId, event.channel);\n };\n await connection.setRemoteDescription(sdp);\n const answer = await connection.createAnswer();\n await connection.setLocalDescription(answer);\n this.signaling.sendSignal(fromPeerId, {\n kind: \"answer\",\n sdp: answer,\n } satisfies SignalingPayload);\n }\n\n private async handleAnswer(fromPeerId: string, sdp: RTCSessionDescriptionInit): Promise<void> {\n const slot = this.slots.get(fromPeerId);\n if (!slot) return;\n await slot.connection.setRemoteDescription(sdp);\n }\n\n private async handleIceCandidate(\n fromPeerId: string,\n candidate: RTCIceCandidateInit\n ): Promise<void> {\n const slot = this.slots.get(fromPeerId);\n if (!slot) return;\n try {\n await slot.connection.addIceCandidate(candidate);\n } catch {\n // Ignore candidate errors — a stale candidate after the connection\n // has already opened is benign.\n }\n }\n\n private wireConnection(peerId: string, connection: RTCPeerConnection): void {\n connection.onicecandidate = (event) => {\n if (event.candidate) {\n this.signaling.sendSignal(peerId, {\n kind: \"ice\",\n candidate: event.candidate.toJSON(),\n } satisfies SignalingPayload);\n }\n };\n connection.onconnectionstatechange = () => {\n const state = connection.connectionState;\n if (state === \"connected\") {\n this.emit(\"peer-candidate\", {\n peerId: peerId as unknown as PeerId,\n peerMetadata: {},\n });\n } else if (state === \"disconnected\" || state === \"failed\" || state === \"closed\") {\n this.slots.delete(peerId);\n this.emit(\"peer-disconnected\", { peerId: peerId as unknown as PeerId });\n }\n };\n }\n\n private wireDataChannel(peerId: string, channel: RTCDataChannel): void {\n channel.onopen = () => {\n const slot = this.slots.get(peerId);\n if (!slot) return;\n // Drain any pending sends that were queued while the channel\n // was still connecting.\n for (const bytes of slot.pendingSends) {\n channel.send(bytes);\n }\n slot.pendingSends = [];\n };\n channel.onmessage = (event) => {\n const data = event.data;\n if (data instanceof ArrayBuffer) {\n this.dispatchMessage(new Uint8Array(data));\n } else if (data instanceof Uint8Array) {\n this.dispatchMessage(data);\n }\n // Other types (strings, Blobs) are ignored — Polly's mesh transport\n // only sends binary payloads via this adapter.\n };\n channel.onclose = () => {\n const slot = this.slots.get(peerId);\n if (slot?.channel === channel) {\n slot.channel = undefined;\n }\n };\n }\n\n private dispatchMessage(bytes: Uint8Array): void {\n try {\n const message = this.deserialiseMessage(bytes);\n this.emit(\"message\", message);\n } catch {\n // Drop malformed messages silently — the MeshNetworkAdapter wrapper\n // also drops on verification failure, so a corrupt frame at this\n // layer is observationally the same as a forgery at the layer above.\n }\n }\n\n /** Pack an Automerge Message into binary for transmission over the\n * data channel. The format mirrors MeshNetworkAdapter's internal\n * serialisation: a length-prefixed JSON header followed by the raw\n * data bytes. Returns a Uint8Array<ArrayBuffer> so the result is\n * directly usable by RTCDataChannel.send under TypeScript's strict\n * buffer-source typing. */\n private serialiseMessage(message: Message): Uint8Array<ArrayBuffer> {\n const headerObj: Record<string, unknown> = {\n type: message.type,\n senderId: message.senderId,\n targetId: message.targetId,\n };\n if (\"documentId\" in message && message.documentId !== undefined) {\n headerObj[\"documentId\"] = message.documentId;\n }\n const headerBytes = new TextEncoder().encode(JSON.stringify(headerObj));\n const dataBytes: Uint8Array =\n \"data\" in message && message.data instanceof Uint8Array ? message.data : new Uint8Array(0);\n const size = 4 + headerBytes.length + dataBytes.length;\n const buffer = new ArrayBuffer(size);\n const out = new Uint8Array(buffer);\n const view = new DataView(buffer);\n view.setUint32(0, headerBytes.length, false);\n out.set(headerBytes, 4);\n out.set(dataBytes, 4 + headerBytes.length);\n return out;\n }\n\n /** Inverse of {@link serialiseMessage}. */\n private deserialiseMessage(bytes: Uint8Array): Message {\n if (bytes.length < 4) {\n throw new Error(\"MeshWebRTCAdapter: message too short to deserialise.\");\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const headerLen = view.getUint32(0, false);\n if (bytes.length < 4 + headerLen) {\n throw new Error(\"MeshWebRTCAdapter: message header truncated.\");\n }\n const header = JSON.parse(new TextDecoder().decode(bytes.subarray(4, 4 + headerLen)));\n const data = bytes.slice(4 + headerLen);\n return { ...header, data } as unknown as Message;\n }\n}\n",
|
|
21
|
+
"/**\n * crdt-state — base machinery for Polly's peer-first state primitives.\n *\n * This module is transport-agnostic: it takes a caller-supplied async factory\n * that produces a ready {@link DocHandle}, binds it bidirectionally to a\n * Preact signal, runs any pending schema migrations on load, and integrates\n * with the primitive-registry and migration-registry guards. Phase 1's\n * $peerState and Phase 2's $meshState both construct these base primitives\n * with their own handle factories — one over Automerge-Repo's WebSocket\n * client adapter, the other over WebRTC — and the base never knows which.\n *\n * The signal-to-handle binding uses an `updating` guard flag to prevent write\n * loops: when a local signal assignment runs the effect that pushes the value\n * into `handle.change`, the flag is raised so that the 'change' event the\n * handle fires back is ignored. The same flag protects in the other direction\n * when a remote change seeds the signal.\n *\n * For the Phase 0 cut, writes are applied with a naive top-level structural\n * replacement inside the `Automerge.change` block. This is correct for\n * JSON-shaped documents with scalar and flat-object fields and is good enough\n * to exercise the rest of the pipeline. The specialised variants for text,\n * counters, and lists (which require type-specific operation capture to\n * preserve concurrent-edit semantics) land in Phase 1's crdt-specialised.ts.\n */\n\nimport type { DocHandle } from \"@automerge/automerge-repo/slim\";\nimport { effect, signal } from \"@preact/signals\";\nimport type { Access } from \"./access\";\nimport { type MigratableState, MigrationError, migrationRegistry } from \"./migrate-primitive\";\nimport { type PrimitiveKind, primitiveRegistry } from \"./primitive-registry\";\nimport {\n type Migrations,\n runMigrations,\n SCHEMA_VERSION_FIELD,\n setDocVersion,\n type VersionedDoc,\n} from \"./schema-version\";\n\n/**\n * The interface a Polly peer-first primitive exposes at the call site. It\n * satisfies {@link MigratableState} so that the cross-primitive migration\n * helper can consume it directly.\n */\nexport interface CrdtPrimitive<T extends VersionedDoc> extends MigratableState<T> {\n /** Stable logical key the primitive was registered under. */\n readonly key: string;\n /** Primitive kind — one of the {@link PrimitiveKind} labels. */\n readonly primitive: PrimitiveKind;\n /** Current value. Writes push into the backing Automerge document. */\n value: T;\n /** Resolves when the handle is ready and migrations have run. */\n readonly loaded: Promise<void>;\n /** The underlying {@link DocHandle}, populated after {@link loaded} resolves.\n * Intended for advanced escape hatches; most callers should stay at the\n * signal surface. */\n readonly handle: DocHandle<T> | undefined;\n}\n\n/**\n * Options for constructing a base CRDT-backed primitive. Phase 1 and Phase 2\n * primitive constructors pass a transport-specific {@link getHandle} factory\n * and their own {@link primitive} label; everything else is shared.\n */\nexport interface CrdtStateOptions<T extends VersionedDoc> {\n /** Stable logical key identifying this piece of state. */\n key: string;\n /** Primitive kind label for registry and error-message purposes. */\n primitive: PrimitiveKind;\n /** Initial value if no stored document exists yet. Applied by the caller's\n * handle factory; the base module does not create documents itself. */\n initialValue: T;\n /** Async factory that resolves to a ready {@link DocHandle}. The factory is\n * responsible for repo lookup, document creation, and any transport-specific\n * setup. The base module calls this once, during hydration. */\n getHandle: () => Promise<DocHandle<T>>;\n /** Target schema version for the application. If set, migrations run on\n * load to bring the document up to this version before the signal hydrates. */\n schemaVersion?: number;\n /** Migration table. Ignored if {@link schemaVersion} is not set. */\n migrations?: Migrations;\n /** Declarative access predicates. Not consumed by the base module; the\n * transport-specific constructors compile it to their enforcement layer. */\n access?: Access;\n /** Optional free-text call-site label for primitive-registry error messages. */\n callSite?: string;\n}\n\n/**\n * Construct a base CRDT-backed Polly primitive. Integrates with\n * primitive-registry (for collision detection), migration-registry (for\n * cross-family migration guards), and schema-version (for on-load migrations).\n *\n * @throws {MigrationError} if the source key has been marked as migrated.\n * @throws {PrimitiveCollisionError} if the key is already registered under a\n * different primitive kind.\n */\nexport function $crdtState<T extends VersionedDoc>(options: CrdtStateOptions<T>): CrdtPrimitive<T> {\n if (migrationRegistry.isMarked(options.key, options.primitive)) {\n throw new MigrationError(\n `Cannot construct $${options.primitive}(\"${options.key}\"): this key has been marked as migrated. Migrations are one-way; use the destination primitive instead.`,\n \"already-migrated\",\n options.key,\n options.primitive\n );\n }\n primitiveRegistry.register(options.key, options.primitive, options.callSite);\n\n const inner = signal<T>(options.initialValue);\n let updating = false;\n let currentHandle: DocHandle<T> | undefined;\n\n const loaded = (async () => {\n const handle = await options.getHandle();\n await handle.whenReady();\n currentHandle = handle;\n\n // Run any pending schema migrations inside a change block so they land\n // as a single Automerge operation set.\n if (options.schemaVersion !== undefined) {\n const targetVersion = options.schemaVersion;\n const migrations = options.migrations ?? {};\n handle.change((doc) => {\n runMigrations(doc as unknown as Record<string, unknown>, targetVersion, migrations);\n // runMigrations stamps the version on every intermediate step; make\n // sure the final value is recorded even when no migrations ran.\n setDocVersion(doc as unknown as Record<string, unknown>, targetVersion);\n });\n }\n\n // Seed the signal with the hydrated doc state. Raise the guard first so\n // the 'change' listener we install below does not echo this write back.\n updating = true;\n try {\n inner.value = cloneDoc(handle.doc());\n } finally {\n updating = false;\n }\n\n // Remote-changes-to-signal binding.\n handle.on(\"change\", (payload) => {\n if (updating) return;\n updating = true;\n try {\n inner.value = cloneDoc(payload.doc);\n } finally {\n updating = false;\n }\n });\n\n // Signal-to-remote binding. The effect runs once on registration with\n // the already-hydrated value; the guard makes that first run a no-op at\n // the handle level because updating is false but the doc already equals\n // the signal, so Automerge records no new operations.\n effect(() => {\n const value = inner.value;\n if (updating) return;\n if (!currentHandle) return;\n updating = true;\n try {\n currentHandle.change((doc) => {\n applyTopLevel(doc as unknown as Record<string, unknown>, value);\n });\n } finally {\n updating = false;\n }\n });\n })();\n\n return {\n key: options.key,\n primitive: options.primitive,\n get value() {\n return inner.value;\n },\n set value(next: T) {\n inner.value = next;\n },\n loaded,\n get handle() {\n return currentHandle;\n },\n };\n}\n\n/**\n * Shallow clone of an Automerge doc into a plain JS object. Automerge docs\n * are Proxies; the signal holds a detached plain-object snapshot so that\n * application code does not accidentally mutate the CRDT through the signal.\n */\nfunction cloneDoc<T>(doc: T): T {\n return JSON.parse(JSON.stringify(doc)) as T;\n}\n\n/**\n * Copy every top-level field from the incoming value onto the Automerge doc.\n * This is the naive Phase 0 write path: correct for flat JSON-shaped documents\n * and good enough for the base module's tests. Specialised primitives in\n * Phase 1 will replace this with type-aware operation capture for text,\n * counters, and lists.\n *\n * The reserved schema-version field is not copied — it is managed by the\n * migration subsystem and must not be overwritten by application writes.\n */\nfunction applyTopLevel<T extends VersionedDoc>(doc: Record<string, unknown>, value: T): void {\n for (const key of Object.keys(value)) {\n if (key === SCHEMA_VERSION_FIELD) continue;\n doc[key] = (value as unknown as Record<string, unknown>)[key];\n }\n}\n",
|
|
22
|
+
"/**\n * mesh-state — Phase 2 wrappers exposing $meshState, $meshText, $meshCounter,\n * and $meshList. These are the application-facing constructors for the\n * strongest resilience tier in RFC-041: every device is a full Automerge\n * replica, the server is *not on the data path at all*, and the application\n * functions with zero server uptime once direct peer connections are\n * established.\n *\n * Each primitive wraps the corresponding Phase 0 base ($crdtState, $crdtText,\n * $crdtCounter, $crdtList) with three additions:\n *\n * 1. The `primitive` label is hard-coded to \"meshState\" so the\n * primitive-registry collision detection knows which family the key\n * belongs to.\n *\n * 2. A handle factory that resolves the application's logical key to an\n * Automerge DocumentId via a per-Repo key map, identical in shape to\n * the $peerState factory but registered against a separate Repo\n * configured for the mesh transport (signed and encrypted at the\n * network layer).\n *\n * 3. Signing and encryption are mandatory, not optional. Where $peerState\n * accepts encrypt/sign as opt-in flags (currently throwing in Phase 1),\n * $meshState requires every operation to be signed by the originating\n * peer's key and encrypted under the document's symmetric key. The\n * mechanism lives in the wrapping MeshNetworkAdapter that the Repo\n * uses for transport.\n *\n * The Repo itself is supplied by the application via {@link configureMeshState}\n * or per-call via the `repo` option. In Phase 2 the production transport will\n * be a WebRTC mesh adapter wrapping signing+encryption around an in-process\n * RTCDataChannel; for tests and for the early Phase 2 cut, an in-memory\n * loopback adapter pair satisfies the same contract.\n */\n\nimport type { DocHandle, DocumentId, Repo } from \"@automerge/automerge-repo/slim\";\nimport type { Access } from \"./access\";\nimport {\n $crdtCounter,\n $crdtList,\n $crdtText,\n type CounterDoc,\n type ListDoc,\n type SpecialisedPrimitive,\n type TextDoc,\n} from \"./crdt-specialised\";\nimport { $crdtState, type CrdtPrimitive } from \"./crdt-state\";\nimport type { Migrations, VersionedDoc } from \"./schema-version\";\n\n/** Common option shape across all four $mesh* primitives. */\nexport interface MeshStateOptions {\n /** Override the default Repo for this primitive. The Repo must be\n * configured with the mesh transport (signing and encryption at the\n * network layer). */\n repo?: Repo;\n /** Schema version target for the application. Migrations run on load. */\n schemaVersion?: number;\n /** Migration table keyed by target version. Required if schemaVersion is set. */\n migrations?: Migrations;\n /** Declarative read/write access. The mesh transport compiles this into\n * a public-key set used by the signing layer to verify incoming ops. */\n access?: Access;\n}\n\n/** Internal: per-Repo key → DocumentId map. */\nconst keyMapsByRepo = new WeakMap<Repo, Map<string, DocumentId>>();\nlet defaultRepo: Repo | undefined;\n\n/**\n * Set the default Repo that the $mesh* primitives use when no `repo` option\n * is supplied. Calling this with a new Repo clears the per-Repo key map so\n * that tests start each scenario with a fresh document space.\n *\n * Production code typically calls this once at application startup with a\n * Repo configured for the mesh transport. Tests call it before each\n * scenario with an in-memory or loopback Repo.\n */\nexport function configureMeshState(repo: Repo): void {\n defaultRepo = repo;\n keyMapsByRepo.set(repo, new Map());\n}\n\n/**\n * Reset the mesh-state subsystem to its initial unconfigured state.\n * Intended for tests; production code should not call this.\n */\nexport function resetMeshState(): void {\n defaultRepo = undefined;\n}\n\nfunction resolveRepo(option: Repo | undefined): Repo {\n const repo = option ?? defaultRepo;\n if (!repo) {\n throw new Error(\n \"Polly $meshState: no Repo configured. Call configureMeshState(repo) at startup or pass { repo } in the primitive options.\"\n );\n }\n return repo;\n}\n\nfunction getKeyMap(repo: Repo): Map<string, DocumentId> {\n let map = keyMapsByRepo.get(repo);\n if (!map) {\n map = new Map();\n keyMapsByRepo.set(repo, map);\n }\n return map;\n}\n\nfunction buildHandleFactory<D>(\n repo: Repo,\n key: string,\n initialDoc: D\n): () => Promise<DocHandle<D>> {\n return async () => {\n const map = getKeyMap(repo);\n const existingId = map.get(key);\n if (existingId !== undefined) {\n return repo.find<D>(existingId);\n }\n const handle = repo.create<D>(initialDoc);\n map.set(key, handle.documentId);\n return handle;\n };\n}\n\n// ─── $meshState ─────────────────────────────────────────────────────────────\n\n/**\n * Create a peer-replicated state primitive backed by Automerge with a mesh\n * transport. Every device holds a full replica; no central server holds a\n * replica. Operations flow peer-to-peer through signed and encrypted\n * messages on the underlying transport.\n *\n * @example\n * ```ts\n * const journal = $meshState<Journal>(\"journal\", { entries: [] });\n * await journal.loaded;\n * journal.value = { entries: [\"my private thoughts\"] };\n * ```\n */\nexport function $meshState<T extends VersionedDoc>(\n key: string,\n initialValue: T,\n options: MeshStateOptions = {}\n): CrdtPrimitive<T> {\n const repo = resolveRepo(options.repo);\n return $crdtState<T>({\n key,\n primitive: \"meshState\",\n initialValue,\n getHandle: buildHandleFactory<T>(repo, key, initialValue),\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n });\n}\n\n// ─── $meshText ──────────────────────────────────────────────────────────────\n\n/**\n * Create a peer-replicated text primitive backed by a mesh transport.\n * Concurrent character-level edits from peers merge cleanly via Automerge's\n * updateText splicing, and every operation is signed and encrypted before\n * leaving the originating peer.\n */\nexport function $meshText(\n key: string,\n initialValue: string,\n options: MeshStateOptions = {}\n): SpecialisedPrimitive<string> {\n const repo = resolveRepo(options.repo);\n return $crdtText(key, initialValue, {\n primitive: \"meshState\",\n getHandle: buildHandleFactory<TextDoc>(repo, key, { text: initialValue }),\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n });\n}\n\n// ─── $meshCounter ───────────────────────────────────────────────────────────\n\n/**\n * Create a peer-replicated counter primitive backed by a mesh transport.\n * Concurrent increments commute, and every operation is signed and\n * encrypted before leaving the originating peer.\n */\nexport function $meshCounter(\n key: string,\n initialValue: number,\n options: MeshStateOptions = {}\n): SpecialisedPrimitive<number> {\n const repo = resolveRepo(options.repo);\n return $crdtCounter(key, initialValue, {\n primitive: \"meshState\",\n getHandle: buildHandleFactory<CounterDoc>(repo, key, {}),\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n });\n}\n\n// ─── $meshList ──────────────────────────────────────────────────────────────\n\n/**\n * Create a peer-replicated list primitive backed by a mesh transport.\n * Phase 0 naive replacement applies to writes for now; Phase 1.1 will\n * refine with structural diff-to-splice for concurrent insert/remove\n * preservation.\n */\nexport function $meshList<T>(\n key: string,\n initialValue: T[],\n options: MeshStateOptions = {}\n): SpecialisedPrimitive<T[]> {\n const repo = resolveRepo(options.repo);\n return $crdtList<T>(key, initialValue, {\n primitive: \"meshState\",\n getHandle: buildHandleFactory<ListDoc<T>>(repo, key, { items: initialValue }),\n schemaVersion: options.schemaVersion,\n migrations: options.migrations,\n access: options.access,\n });\n}\n",
|
|
23
|
+
"/**\n * mesh-webrtc-adapter — Phase 2 browser-side WebRTC transport for Polly's\n * $meshState primitive. Extends Automerge's NetworkAdapter base class and\n * uses real native RTCPeerConnection / RTCDataChannel instances to carry\n * sync messages between peers.\n *\n * This is the \"base\" transport that MeshNetworkAdapter wraps with its\n * sign-then-encrypt envelope. The stack is:\n *\n * $meshState\n * └─ Repo\n * └─ MeshNetworkAdapter (sign + encrypt)\n * └─ MeshWebRTCAdapter (real data channels)\n * └─ MeshSignalingClient (SDP/ICE relay)\n * └─ signalingServer (Elysia plugin on the host app)\n *\n * Because WebRTC lives in browsers, this module is browser-only. It\n * assumes global RTCPeerConnection, RTCDataChannel, and WebSocket types\n * are available. Under bun:test the classes cannot be exercised\n * end-to-end — the first validation path is either Playwright running a\n * real browser, a Puppeteer harness, or a human testing a browser-side\n * example app that consumes the adapter.\n *\n * What this module does at runtime:\n *\n * - Constructs a MeshWebRTCAdapter with a signalling client and a local\n * peer id. No data channels exist at construction time.\n *\n * - When Automerge's NetworkSubsystem calls connect(peerId) on the\n * adapter, it starts listening for signals from remote peers and is\n * ready to build peer connections as they are discovered.\n *\n * - When a remote peer sends an SDP offer via the signalling channel,\n * the adapter builds a fresh RTCPeerConnection, accepts the offer,\n * produces an answer, sends it back through signalling, and wires the\n * received data channel to emit Automerge Message events upward.\n *\n * - When the local repo calls send(message), the adapter looks up the\n * peer connection for message.targetId and writes the serialised\n * bytes to its data channel. If no connection exists yet, the adapter\n * creates one by sending an SDP offer through signalling. Outgoing\n * messages are queued until the channel is open.\n *\n * - Disconnect tears down every peer connection and closes the\n * signalling client.\n */\n\nimport {\n type Message,\n NetworkAdapter,\n type PeerId,\n type PeerMetadata,\n} from \"@automerge/automerge-repo/slim\";\nimport { isBlobMessageType } from \"./blob-transfer\";\nimport type { MeshSignalingClient } from \"./mesh-signaling-client\";\n\n/** Standard STUN servers for NAT traversal. In production, callers who\n * need TURN fallback for peers behind symmetric NATs should replace this\n * with their own ICE server list. */\nexport const DEFAULT_ICE_SERVERS: RTCIceServer[] = [\n { urls: \"stun:stun.l.google.com:19302\" },\n { urls: \"stun:stun1.l.google.com:19302\" },\n];\n\n/** Options for constructing a {@link MeshWebRTCAdapter}. */\nexport interface MeshWebRTCAdapterOptions {\n /** The signalling client the adapter uses to exchange SDP and ICE\n * candidates with other peers. Typically constructed once per\n * application and shared across any adapters that need it. */\n signaling: MeshSignalingClient;\n /** The local peer id. Must match the peer id the signalling client\n * registered with. */\n peerId: string;\n /** Peer ids to connect to on startup. When `connect()` is called, the\n * adapter iterates this list and initiates a WebRTC connection to each\n * one by sending an SDP offer through the signalling channel. Peers\n * not in this list can still connect by sending an offer *to* this\n * adapter. The natural source for this list is the keyring's\n * knownPeers map keys. */\n knownPeerIds?: string[];\n /** Optional ICE server list override. Defaults to {@link DEFAULT_ICE_SERVERS}. */\n iceServers?: RTCIceServer[];\n /** Optional data channel label. Defaults to \"polly-mesh\". Applications\n * that share a signalling server between multiple meshes may want\n * distinct labels per mesh. */\n dataChannelLabel?: string;\n /** RTCPeerConnection constructor. Defaults to\n * `globalThis.RTCPeerConnection`. Inject a different implementation\n * (e.g. `werift` or `@roamhq/wrtc`) when running outside a browser, or\n * to use a custom subclass for tests or instrumentation. */\n RTCPeerConnection?: typeof RTCPeerConnection;\n}\n\n/** Types of signalling payload this adapter exchanges through the\n * signalling channel. The signalling server treats these as opaque. */\ntype SignalingPayload =\n | { kind: \"offer\"; sdp: RTCSessionDescriptionInit }\n | { kind: \"answer\"; sdp: RTCSessionDescriptionInit }\n | { kind: \"ice\"; candidate: RTCIceCandidateInit };\n\n/** State tracked for each remote peer. */\ninterface PeerSlot {\n connection: RTCPeerConnection;\n channel: RTCDataChannel | undefined;\n /** Outgoing messages queued while the channel is still connecting.\n * Typed as ArrayBuffer-backed so they are directly usable by\n * RTCDataChannel.send under TypeScript's strict buffer-source typing. */\n pendingSends: Uint8Array<ArrayBuffer>[];\n}\n\n/**\n * Automerge-Repo NetworkAdapter backed by real WebRTC data channels.\n * Manages one RTCPeerConnection per remote peer and uses a supplied\n * {@link MeshSignalingClient} for SDP/ICE exchange.\n */\nexport class MeshWebRTCAdapter extends NetworkAdapter {\n readonly signaling: MeshSignalingClient;\n readonly iceServers: RTCIceServer[];\n readonly dataChannelLabel: string;\n readonly knownPeerIds: string[];\n private readonly RTCPeerConnectionCtor: typeof RTCPeerConnection;\n private readonly slots = new Map<string, PeerSlot>();\n private ready = false;\n private readyResolver: (() => void) | undefined;\n\n /** Callback for incoming blob messages. Set by the blob store.\n * Called with the sender's peer ID, the raw header object, and the\n * binary payload (chunk data). */\n onBlobMessage?: (peerId: string, header: Record<string, unknown>, data: Uint8Array) => void;\n\n constructor(options: MeshWebRTCAdapterOptions) {\n super();\n this.signaling = options.signaling;\n this.iceServers = options.iceServers ?? DEFAULT_ICE_SERVERS;\n this.dataChannelLabel = options.dataChannelLabel ?? \"polly-mesh\";\n this.knownPeerIds = options.knownPeerIds ?? [];\n const PC = options.RTCPeerConnection ?? globalThis.RTCPeerConnection;\n if (typeof PC !== \"function\") {\n throw new Error(\n \"MeshWebRTCAdapter: no RTCPeerConnection implementation found. Pass one via options.RTCPeerConnection (e.g. from `werift` or `@roamhq/wrtc`), or run in a browser where `globalThis.RTCPeerConnection` exists.\"\n );\n }\n this.RTCPeerConnectionCtor = PC;\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n whenReady(): Promise<void> {\n if (this.ready) return Promise.resolve();\n return new Promise((resolve) => {\n this.readyResolver = resolve;\n });\n }\n\n /**\n * Start the adapter. Wires the signalling client's onSignal callback\n * to the adapter's dispatch, opens the signalling connection if it\n * is not already open, and marks the adapter ready.\n */\n connect(peerId: PeerId, peerMetadata?: PeerMetadata): void {\n this.peerId = peerId;\n if (peerMetadata !== undefined) {\n this.peerMetadata = peerMetadata;\n }\n this.ready = true;\n this.readyResolver?.();\n\n // Initiate WebRTC connections to every known peer. This is the\n // discovery step: once the SDP exchange completes and the data\n // channel opens, the adapter emits 'peer-candidate' and the Repo's\n // NetworkSubsystem learns about the peer.\n for (const remotePeerId of this.knownPeerIds) {\n if (remotePeerId !== (peerId as unknown as string) && !this.slots.has(remotePeerId)) {\n this.createInitiatingSlot(remotePeerId);\n }\n }\n }\n\n disconnect(): void {\n for (const slot of this.slots.values()) {\n slot.channel?.close();\n slot.connection.close();\n }\n this.slots.clear();\n this.signaling.close();\n this.ready = false;\n this.emit(\"close\");\n }\n\n /**\n * Send a sync message to a specific remote peer. If no RTCPeerConnection\n * exists yet, the adapter initiates one by producing an SDP offer and\n * sending it through the signalling channel; the outgoing bytes are\n * queued until the data channel is open.\n */\n send(message: Message): void {\n const targetId = message.targetId as unknown as string;\n const bytes = this.serialiseMessage(message);\n let slot = this.slots.get(targetId);\n if (!slot) {\n slot = this.createInitiatingSlot(targetId);\n }\n if (slot.channel && slot.channel.readyState === \"open\") {\n slot.channel.send(bytes);\n } else {\n slot.pendingSends.push(bytes);\n }\n }\n\n /**\n * Entry point the signalling client calls when it receives a signal\n * from a remote peer. Dispatches on the payload `kind` to either\n * accept an offer (building an answer), apply an answer, or add an\n * ICE candidate. Exposed publicly so the caller that constructs the\n * signalling client can wire the onSignal callback directly to this\n * method.\n */\n handleSignal(fromPeerId: string, rawPayload: unknown): void {\n const payload = rawPayload as unknown as SignalingPayload;\n if (!payload || typeof payload !== \"object\" || !(\"kind\" in payload)) {\n return;\n }\n switch (payload.kind) {\n case \"offer\":\n void this.handleOffer(fromPeerId, payload.sdp);\n return;\n case \"answer\":\n void this.handleAnswer(fromPeerId, payload.sdp);\n return;\n case \"ice\":\n void this.handleIceCandidate(fromPeerId, payload.candidate);\n return;\n }\n }\n\n private createInitiatingSlot(targetId: string): PeerSlot {\n const connection = new this.RTCPeerConnectionCtor({ iceServers: this.iceServers });\n const channel = connection.createDataChannel(this.dataChannelLabel, { ordered: true });\n const slot: PeerSlot = { connection, channel, pendingSends: [] };\n this.slots.set(targetId, slot);\n this.wireConnection(targetId, connection);\n this.wireDataChannel(targetId, channel);\n void this.initiateOffer(targetId, connection);\n return slot;\n }\n\n private async initiateOffer(targetId: string, connection: RTCPeerConnection): Promise<void> {\n const offer = await connection.createOffer();\n await connection.setLocalDescription(offer);\n this.signaling.sendSignal(targetId, { kind: \"offer\", sdp: offer } satisfies SignalingPayload);\n }\n\n private async handleOffer(fromPeerId: string, sdp: RTCSessionDescriptionInit): Promise<void> {\n const existing = this.slots.get(fromPeerId);\n if (existing) {\n // Glare: we already initiated a connection to this peer. Resolve by\n // peer-id ordering: the lexicographically lower id yields its own\n // offer and accepts the incoming one. The higher id ignores the\n // incoming offer and waits for the answer to its own.\n const localId = this.peerId as unknown as string;\n if (localId > fromPeerId) {\n return;\n }\n existing.channel?.close();\n existing.connection.close();\n this.slots.delete(fromPeerId);\n }\n\n const connection = new this.RTCPeerConnectionCtor({ iceServers: this.iceServers });\n const slot: PeerSlot = { connection, channel: undefined, pendingSends: [] };\n this.slots.set(fromPeerId, slot);\n this.wireConnection(fromPeerId, connection);\n connection.ondatachannel = (event) => {\n slot.channel = event.channel;\n this.wireDataChannel(fromPeerId, event.channel);\n };\n await connection.setRemoteDescription(sdp);\n const answer = await connection.createAnswer();\n await connection.setLocalDescription(answer);\n this.signaling.sendSignal(fromPeerId, {\n kind: \"answer\",\n sdp: answer,\n } satisfies SignalingPayload);\n }\n\n private async handleAnswer(fromPeerId: string, sdp: RTCSessionDescriptionInit): Promise<void> {\n const slot = this.slots.get(fromPeerId);\n if (!slot) return;\n await slot.connection.setRemoteDescription(sdp);\n }\n\n private async handleIceCandidate(\n fromPeerId: string,\n candidate: RTCIceCandidateInit\n ): Promise<void> {\n const slot = this.slots.get(fromPeerId);\n if (!slot) return;\n try {\n await slot.connection.addIceCandidate(candidate);\n } catch {\n // Ignore candidate errors — a stale candidate after the connection\n // has already opened is benign.\n }\n }\n\n private wireConnection(peerId: string, connection: RTCPeerConnection): void {\n connection.onicecandidate = (event) => {\n if (event.candidate) {\n this.signaling.sendSignal(peerId, {\n kind: \"ice\",\n candidate: event.candidate.toJSON(),\n } satisfies SignalingPayload);\n }\n };\n connection.onconnectionstatechange = () => {\n const state = connection.connectionState;\n if (state === \"connected\") {\n this.emit(\"peer-candidate\", {\n peerId: peerId as unknown as PeerId,\n peerMetadata: {},\n });\n } else if (state === \"disconnected\" || state === \"failed\" || state === \"closed\") {\n this.slots.delete(peerId);\n this.emit(\"peer-disconnected\", { peerId: peerId as unknown as PeerId });\n }\n };\n }\n\n private wireDataChannel(peerId: string, channel: RTCDataChannel): void {\n channel.onopen = () => {\n const slot = this.slots.get(peerId);\n if (!slot) return;\n // Drain any pending sends that were queued while the channel\n // was still connecting.\n for (const bytes of slot.pendingSends) {\n channel.send(bytes);\n }\n slot.pendingSends = [];\n };\n channel.onmessage = (event) => {\n const data = event.data;\n if (data instanceof ArrayBuffer) {\n this.dispatchMessage(peerId, new Uint8Array(data));\n } else if (data instanceof Uint8Array) {\n this.dispatchMessage(peerId, data);\n }\n // Other types (strings, Blobs) are ignored — Polly's mesh transport\n // only sends binary payloads via this adapter.\n };\n channel.onclose = () => {\n const slot = this.slots.get(peerId);\n if (slot?.channel === channel) {\n slot.channel = undefined;\n }\n };\n }\n\n private dispatchMessage(fromPeerId: string, bytes: Uint8Array): void {\n try {\n // Intercept blob messages before they reach the Automerge deserialiser.\n // Blob headers have type fields starting with \"blob-\" and would fail\n // MeshNetworkAdapter's signed-envelope unwrap if passed through.\n if (this.onBlobMessage && isBlobMessageType(bytes)) {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const headerLen = view.getUint32(0, false);\n const header = JSON.parse(\n new TextDecoder().decode(bytes.subarray(4, 4 + headerLen))\n ) as Record<string, unknown>;\n const data = bytes.subarray(4 + headerLen);\n this.onBlobMessage(fromPeerId, header, data);\n return;\n }\n const message = this.deserialiseMessage(bytes);\n this.emit(\"message\", message);\n } catch {\n // Drop malformed messages silently — the MeshNetworkAdapter wrapper\n // also drops on verification failure, so a corrupt frame at this\n // layer is observationally the same as a forgery at the layer above.\n }\n }\n\n /** Peer IDs with an open data channel, suitable for blob requests. */\n get connectedPeerIds(): string[] {\n const ids: string[] = [];\n for (const [peerId, slot] of this.slots) {\n if (slot.channel && slot.channel.readyState === \"open\") {\n ids.push(peerId);\n }\n }\n return ids;\n }\n\n /** Send a pre-serialised blob message to a specific peer. Returns false\n * if the peer is not connected or the send buffer is above the high-water\n * mark (caller should retry after a delay). */\n sendBlobMessage(peerId: string, bytes: Uint8Array<ArrayBuffer>): boolean {\n const slot = this.slots.get(peerId);\n if (!slot?.channel || slot.channel.readyState !== \"open\") return false;\n return this.trySendOnChannel(slot.channel, bytes);\n }\n\n /** Send bytes on a data channel if the buffer is below the high-water\n * mark. Returns true if sent, false if backpressure applies. */\n private trySendOnChannel(channel: RTCDataChannel, bytes: Uint8Array<ArrayBuffer>): boolean {\n // 256 KiB high-water mark — matches the blob transfer default.\n if (channel.bufferedAmount > 256 * 1024) return false;\n channel.send(bytes);\n return true;\n }\n\n /** Pack an Automerge Message into binary for transmission over the\n * data channel. The format mirrors MeshNetworkAdapter's internal\n * serialisation: a length-prefixed JSON header followed by the raw\n * data bytes. Returns a Uint8Array<ArrayBuffer> so the result is\n * directly usable by RTCDataChannel.send under TypeScript's strict\n * buffer-source typing. */\n private serialiseMessage(message: Message): Uint8Array<ArrayBuffer> {\n const headerObj: Record<string, unknown> = {\n type: message.type,\n senderId: message.senderId,\n targetId: message.targetId,\n };\n if (\"documentId\" in message && message.documentId !== undefined) {\n headerObj[\"documentId\"] = message.documentId;\n }\n const headerBytes = new TextEncoder().encode(JSON.stringify(headerObj));\n const dataBytes: Uint8Array =\n \"data\" in message && message.data instanceof Uint8Array ? message.data : new Uint8Array(0);\n const size = 4 + headerBytes.length + dataBytes.length;\n const buffer = new ArrayBuffer(size);\n const out = new Uint8Array(buffer);\n const view = new DataView(buffer);\n view.setUint32(0, headerBytes.length, false);\n out.set(headerBytes, 4);\n out.set(dataBytes, 4 + headerBytes.length);\n return out;\n }\n\n /** Inverse of {@link serialiseMessage}. */\n private deserialiseMessage(bytes: Uint8Array): Message {\n if (bytes.length < 4) {\n throw new Error(\"MeshWebRTCAdapter: message too short to deserialise.\");\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const headerLen = view.getUint32(0, false);\n if (bytes.length < 4 + headerLen) {\n throw new Error(\"MeshWebRTCAdapter: message header truncated.\");\n }\n const header = JSON.parse(new TextDecoder().decode(bytes.subarray(4, 4 + headerLen)));\n const data = bytes.slice(4 + headerLen);\n return { ...header, data } as unknown as Message;\n }\n}\n",
|
|
16
24
|
"/**\n * pairing — Phase 2 first-cut pairing flow for $meshState.\n *\n * Two devices that want to share a $meshState document must exchange three\n * things before sync can begin: the issuer's Ed25519 signing public key\n * (so the receiver can verify ops authored by the issuer), the symmetric\n * document encryption key (so both sides can encrypt and decrypt the\n * shared document), and the issuer's stable peer id (so the receiver\n * knows which entry in its keyring the public key belongs to). This\n * module packs all three into a {@link PairingToken}, serialises it to a\n * compact binary format suitable for QR codes or copy-paste, and provides\n * the matching parse-and-apply flow on the receiving side.\n *\n * Threat model: pairing tokens are transmitted over an out-of-band channel\n * that the user can authenticate visually — typically a QR code on the\n * issuer's device, scanned by the receiver. Because anyone with the token\n * can decrypt and impersonate, the OOB channel is the only authentication.\n * The token includes a TTL (default 10 minutes) so that a token displayed\n * briefly and then dismissed cannot be replayed by an attacker who later\n * gains access to a screenshot. A production deployment would layer a\n * Short Authentication String (SAS) on top — both devices display a code\n * derived from the shared state, and the user verifies they match — but\n * that is a follow-up.\n *\n * The pairing flow is one-way in the Phase 2 first cut. The issuer\n * generates a token and displays it; the receiver applies it and picks\n * up the issuer's keys. The receiver's own keys reach the issuer through\n * the access set: when the receiver sends its first signed op, the issuer\n * records the receiver's public key alongside its peer id and adds it to\n * the keyring. A bidirectional pairing flow that exchanges both sides'\n * keys in a single QR exchange is straightforward to add later but adds\n * UX surface area that is not needed for the mesh transport to work.\n */\n\nimport { KEY_BYTES as ENCRYPTION_KEY_BYTES, generateDocumentKey } from \"./encryption\";\nimport type { MeshKeyring } from \"./mesh-network-adapter\";\nimport {\n generateSigningKeyPair,\n PUBLIC_KEY_BYTES as SIGNING_PUBLIC_KEY_BYTES,\n type SigningKeyPair,\n} from \"./signing\";\n\n/** Current pairing-token format version. Bumped if the wire format changes. */\nexport const PAIRING_TOKEN_VERSION = 1;\n\n/** Magic header bytes for sanity-checking parsed tokens. ASCII \"PPT1\". */\nexport const PAIRING_TOKEN_MAGIC = new Uint8Array([0x50, 0x50, 0x54, 0x31]);\n\n/** Length of the random nonce embedded in every token. */\nexport const PAIRING_NONCE_BYTES = 16;\n\n/** Default TTL applied when {@link createPairingToken} is called without an\n * explicit `ttlMs` option. */\nexport const DEFAULT_PAIRING_TTL_MS = 10 * 60 * 1000; // 10 minutes\n\n/**\n * The contents of a pairing token. Both sides operate on this shape; the\n * binary serialisation is purely for transport.\n */\nexport interface PairingToken {\n /** Format version. {@link PAIRING_TOKEN_VERSION} at the time of writing. */\n version: number;\n /** Stable peer id of the issuing device. The receiver records this as\n * the lookup key for the issuer's public key in its keyring. */\n issuerPeerId: string;\n /** Issuer's Ed25519 signing public key (32 bytes). */\n issuerPublicKey: Uint8Array;\n /** Shared document encryption key (32 bytes). The receiver stores this\n * under {@link documentKeyId} in its keyring. */\n documentKey: Uint8Array;\n /** Identifier under which the receiver stores the document key. For the\n * Phase 2 first cut this is typically the well-known DEFAULT_MESH_KEY_ID\n * from mesh-network-adapter; per-document keys (one entry per Automerge\n * document) are a follow-up. */\n documentKeyId: string;\n /** Unix timestamp (milliseconds) after which the token is considered\n * expired and {@link applyPairingToken} refuses to use it. */\n expiresAt: number;\n /** 16-byte random nonce. Carried through serialisation so two tokens\n * with otherwise-identical contents are still distinguishable. */\n nonce: Uint8Array;\n}\n\n/** Errors thrown by the pairing subsystem. */\nexport class PairingError extends Error {\n readonly code:\n | \"expired\"\n | \"wrong-magic\"\n | \"unknown-version\"\n | \"truncated\"\n | \"invalid-public-key\"\n | \"invalid-document-key\"\n | \"invalid-nonce\";\n\n constructor(message: string, code: PairingError[\"code\"]) {\n super(message);\n this.name = \"PairingError\";\n this.code = code;\n }\n}\n\n/**\n * Options for {@link createPairingToken}. The signing identity and the\n * document key are required; everything else is optional with sensible\n * defaults.\n */\nexport interface CreatePairingTokenOptions {\n /** The issuing device's signing keypair. Only the public key ends up in\n * the token; the secret never leaves the issuer. */\n identity: SigningKeyPair;\n /** Stable peer id for the issuing device. */\n issuerPeerId: string;\n /** The symmetric document key the receiver should adopt. If omitted, a\n * fresh key is generated and the caller is responsible for using the\n * same key on the issuing side too. */\n documentKey?: Uint8Array;\n /** Identifier under which the receiver stores the document key. */\n documentKeyId: string;\n /** Time-to-live in milliseconds. Defaults to {@link DEFAULT_PAIRING_TTL_MS}. */\n ttlMs?: number;\n /** Override the current time. Intended for tests; production code should\n * not pass this. */\n now?: () => number;\n}\n\n/**\n * Generate a fresh {@link PairingToken}. The token is ready to be\n * serialised and displayed to the receiver via an OOB channel.\n */\nexport function createPairingToken(options: CreatePairingTokenOptions): PairingToken {\n const now = options.now ? options.now() : Date.now();\n const ttlMs = options.ttlMs ?? DEFAULT_PAIRING_TTL_MS;\n const documentKey = options.documentKey ?? generateDocumentKey();\n const nonce = randomBytes(PAIRING_NONCE_BYTES);\n\n return {\n version: PAIRING_TOKEN_VERSION,\n issuerPeerId: options.issuerPeerId,\n issuerPublicKey: options.identity.publicKey,\n documentKey,\n documentKeyId: options.documentKeyId,\n expiresAt: now + ttlMs,\n nonce,\n };\n}\n\n/**\n * Generate a fresh pairing token *and* a fresh signing keypair in one call.\n * Convenience for first-time setup where the device has no existing\n * identity yet. Returns both so the caller can persist the keypair and\n * then display the token.\n */\nexport function createPairingTokenWithFreshIdentity(args: {\n issuerPeerId: string;\n documentKeyId: string;\n ttlMs?: number;\n now?: () => number;\n}): { identity: SigningKeyPair; token: PairingToken } {\n const identity = generateSigningKeyPair();\n const token = createPairingToken({\n identity,\n issuerPeerId: args.issuerPeerId,\n documentKeyId: args.documentKeyId,\n ttlMs: args.ttlMs,\n now: args.now,\n });\n return { identity, token };\n}\n\n/**\n * Check whether a token has expired against the current wall-clock time\n * (or an injected `now`).\n */\nexport function isPairingTokenExpired(token: PairingToken, now?: () => number): boolean {\n const t = now ? now() : Date.now();\n return t >= token.expiresAt;\n}\n\n/**\n * Apply a parsed and validated token to a {@link MeshKeyring}. Mutates the\n * keyring in place: adds the issuer's public key to {@link MeshKeyring.knownPeers}\n * and the document key to {@link MeshKeyring.documentKeys}.\n *\n * Throws {@link PairingError} with code \"expired\" if the token's TTL has\n * elapsed. The receiver is expected to apply the token promptly after\n * scanning; rejecting expired tokens prevents replay of long-lived\n * captures.\n */\nexport function applyPairingToken(\n token: PairingToken,\n keyring: MeshKeyring,\n options: { now?: () => number } = {}\n): void {\n if (isPairingTokenExpired(token, options.now)) {\n throw new PairingError(\n `Pairing token from ${token.issuerPeerId} expired at ${new Date(token.expiresAt).toISOString()}.`,\n \"expired\"\n );\n }\n keyring.knownPeers.set(token.issuerPeerId, token.issuerPublicKey);\n keyring.documentKeys.set(token.documentKeyId, token.documentKey);\n}\n\n// ─── binary serialisation ──────────────────────────────────────────────────\n\n/**\n * Serialise a token to a binary blob. The wire format is:\n *\n * [4 bytes: magic \"PPT1\"]\n * [1 byte: version]\n * [4 bytes BE: issuer id byte length]\n * [N bytes: issuer id UTF-8]\n * [32 bytes: issuer public key]\n * [32 bytes: document key]\n * [4 bytes BE: document key id byte length]\n * [M bytes: document key id UTF-8]\n * [8 bytes BE: expiresAt (uint64 milliseconds)]\n * [16 bytes: nonce]\n *\n * Use {@link encodePairingToken} to round-trip through a base64 string.\n */\nexport function serialisePairingToken(token: PairingToken): Uint8Array {\n validateForSerialisation(token);\n const issuerBytes = new TextEncoder().encode(token.issuerPeerId);\n const keyIdBytes = new TextEncoder().encode(token.documentKeyId);\n\n const total =\n PAIRING_TOKEN_MAGIC.length +\n 1 + // version\n 4 + // issuer id length\n issuerBytes.length +\n SIGNING_PUBLIC_KEY_BYTES +\n ENCRYPTION_KEY_BYTES +\n 4 + // doc key id length\n keyIdBytes.length +\n 8 + // expiresAt\n PAIRING_NONCE_BYTES;\n\n const out = new Uint8Array(total);\n let offset = 0;\n\n out.set(PAIRING_TOKEN_MAGIC, offset);\n offset += PAIRING_TOKEN_MAGIC.length;\n\n out[offset] = token.version;\n offset += 1;\n\n const view = new DataView(out.buffer);\n view.setUint32(offset, issuerBytes.length, false);\n offset += 4;\n out.set(issuerBytes, offset);\n offset += issuerBytes.length;\n\n out.set(token.issuerPublicKey, offset);\n offset += SIGNING_PUBLIC_KEY_BYTES;\n\n out.set(token.documentKey, offset);\n offset += ENCRYPTION_KEY_BYTES;\n\n view.setUint32(offset, keyIdBytes.length, false);\n offset += 4;\n out.set(keyIdBytes, offset);\n offset += keyIdBytes.length;\n\n // Write expiresAt as uint64 BE. JavaScript numbers are float64 but the\n // value is an integer count of milliseconds, well within 53-bit safe\n // range for any practical timestamp.\n view.setBigUint64(offset, BigInt(token.expiresAt), false);\n offset += 8;\n\n out.set(token.nonce, offset);\n offset += PAIRING_NONCE_BYTES;\n\n return out;\n}\n\n/**\n * Inverse of {@link serialisePairingToken}. Throws {@link PairingError} on\n * malformed input.\n */\nexport function parsePairingToken(bytes: Uint8Array): PairingToken {\n let offset = 0;\n\n // Magic\n if (bytes.length < PAIRING_TOKEN_MAGIC.length) {\n throw new PairingError(`Pairing token too short: ${bytes.length} bytes.`, \"truncated\");\n }\n for (let i = 0; i < PAIRING_TOKEN_MAGIC.length; i++) {\n if (bytes[offset + i] !== PAIRING_TOKEN_MAGIC[i]) {\n throw new PairingError(\n `Pairing token magic mismatch: not a Polly pairing token.`,\n \"wrong-magic\"\n );\n }\n }\n offset += PAIRING_TOKEN_MAGIC.length;\n\n // Version\n if (bytes.length < offset + 1) {\n throw new PairingError(\"Pairing token truncated at version.\", \"truncated\");\n }\n const version = bytes[offset] as unknown as number;\n offset += 1;\n if (version !== PAIRING_TOKEN_VERSION) {\n throw new PairingError(\n `Unknown pairing token version: ${version}. This Polly build supports version ${PAIRING_TOKEN_VERSION}.`,\n \"unknown-version\"\n );\n }\n\n // Issuer id\n if (bytes.length < offset + 4) {\n throw new PairingError(\"Pairing token truncated at issuer id length.\", \"truncated\");\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const issuerLen = view.getUint32(offset, false);\n offset += 4;\n if (bytes.length < offset + issuerLen) {\n throw new PairingError(\"Pairing token truncated at issuer id.\", \"truncated\");\n }\n const issuerPeerId = new TextDecoder().decode(bytes.subarray(offset, offset + issuerLen));\n offset += issuerLen;\n\n // Issuer public key\n if (bytes.length < offset + SIGNING_PUBLIC_KEY_BYTES) {\n throw new PairingError(\"Pairing token truncated at public key.\", \"truncated\");\n }\n const issuerPublicKey = bytes.slice(offset, offset + SIGNING_PUBLIC_KEY_BYTES);\n offset += SIGNING_PUBLIC_KEY_BYTES;\n\n // Document key\n if (bytes.length < offset + ENCRYPTION_KEY_BYTES) {\n throw new PairingError(\"Pairing token truncated at document key.\", \"truncated\");\n }\n const documentKey = bytes.slice(offset, offset + ENCRYPTION_KEY_BYTES);\n offset += ENCRYPTION_KEY_BYTES;\n\n // Document key id\n if (bytes.length < offset + 4) {\n throw new PairingError(\"Pairing token truncated at document key id length.\", \"truncated\");\n }\n const keyIdLen = view.getUint32(offset, false);\n offset += 4;\n if (bytes.length < offset + keyIdLen) {\n throw new PairingError(\"Pairing token truncated at document key id.\", \"truncated\");\n }\n const documentKeyId = new TextDecoder().decode(bytes.subarray(offset, offset + keyIdLen));\n offset += keyIdLen;\n\n // Expires at\n if (bytes.length < offset + 8) {\n throw new PairingError(\"Pairing token truncated at expiry.\", \"truncated\");\n }\n const expiresAtBig = view.getBigUint64(offset, false);\n offset += 8;\n const expiresAt = Number(expiresAtBig);\n\n // Nonce\n if (bytes.length < offset + PAIRING_NONCE_BYTES) {\n throw new PairingError(\"Pairing token truncated at nonce.\", \"truncated\");\n }\n const nonce = bytes.slice(offset, offset + PAIRING_NONCE_BYTES);\n offset += PAIRING_NONCE_BYTES;\n\n return {\n version,\n issuerPeerId,\n issuerPublicKey,\n documentKey,\n documentKeyId,\n expiresAt,\n nonce,\n };\n}\n\n/**\n * Serialise a token and base64-encode it for QR-code or copy-paste display.\n * The encoding uses the standard base64 alphabet (not URL-safe) because\n * QR codes encode bytes directly and do not care about URL safety.\n */\nexport function encodePairingToken(token: PairingToken): string {\n const bytes = serialisePairingToken(token);\n // btoa expects a binary string. Convert via String.fromCharCode per byte;\n // safe for the ~150-byte token size and avoids the spread-into-fromCharCode\n // pattern that runs into argument-count limits on large arrays.\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary);\n}\n\n/**\n * Decode a base64-encoded pairing token produced by {@link encodePairingToken}.\n * Throws {@link PairingError} on malformed input.\n */\nexport function decodePairingToken(encoded: string): PairingToken {\n let binary: string;\n try {\n binary = atob(encoded);\n } catch {\n throw new PairingError(\"Pairing token is not valid base64.\", \"wrong-magic\");\n }\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return parsePairingToken(bytes);\n}\n\n// ─── helpers ───────────────────────────────────────────────────────────────\n\nfunction validateForSerialisation(token: PairingToken): void {\n if (token.issuerPublicKey.length !== SIGNING_PUBLIC_KEY_BYTES) {\n throw new PairingError(\n `Issuer public key must be ${SIGNING_PUBLIC_KEY_BYTES} bytes, got ${token.issuerPublicKey.length}.`,\n \"invalid-public-key\"\n );\n }\n if (token.documentKey.length !== ENCRYPTION_KEY_BYTES) {\n throw new PairingError(\n `Document key must be ${ENCRYPTION_KEY_BYTES} bytes, got ${token.documentKey.length}.`,\n \"invalid-document-key\"\n );\n }\n if (token.nonce.length !== PAIRING_NONCE_BYTES) {\n throw new PairingError(\n `Nonce must be ${PAIRING_NONCE_BYTES} bytes, got ${token.nonce.length}.`,\n \"invalid-nonce\"\n );\n }\n}\n\nfunction randomBytes(n: number): Uint8Array {\n const out = new Uint8Array(n);\n crypto.getRandomValues(out);\n return out;\n}\n",
|
|
17
25
|
"/**\n * revocation — Phase 2 key-revocation flow for $meshState.\n *\n * A $meshState deployment is only as secure as the keys it trusts. When a\n * device is lost, stolen, or compromised, its public key needs to be\n * removed from every other peer's trust set so that any operations signed\n * by that key are refused. This module provides the primitive pieces for\n * that flow: a signed revocation record that names the revoked peer id,\n * carries the issuer's identity, and travels between peers through the\n * same channels as any other signed message.\n *\n * A RevocationRecord is produced by a peer that has decided to revoke\n * another peer (typically because the user told their device to do so).\n * It is then serialised to a binary envelope, broadcast to other peers\n * through whatever transport is available, and applied to each receiving\n * keyring via {@link applyRevocation}. Applying a revocation adds the\n * target peer id to the keyring's {@link MeshKeyring.revokedPeers} set;\n * the MeshNetworkAdapter then drops all further messages from that peer,\n * even though the public key is still present in the knownPeers map.\n *\n * Signature layer: every revocation is signed by the *issuer's* key. The\n * receiver verifies the signature against the issuer's public key before\n * applying. This prevents a peer with no authority from forging a\n * revocation, at least in the signature-level threat model — a compromised\n * peer could still forge revocations naming any other peer, which is why\n * production deployments layer an access-set check on top (\"who is\n * authorised to revoke whom\"). That layer is a follow-up; the first cut\n * is signature-only, and any signed revocation from a known peer is\n * accepted.\n *\n * Why revocations are signed but not encrypted: revocations are public\n * in the sense that they name \"peer X is no longer trusted.\" There is no\n * confidentiality requirement — anyone seeing a revocation learns only\n * that someone has been kicked out, which is already observable from\n * the absence of their operations. Encrypting them would add cost without\n * adding security, so the wire format is signed-only.\n */\n\nimport type { MeshKeyring } from \"./mesh-network-adapter\";\nimport {\n decodeSignedEnvelope,\n encodeSignedEnvelope,\n openEnvelope as openSignedEnvelope,\n type SigningKeyPair,\n signEnvelope,\n} from \"./signing\";\n\n/** Current revocation-record format version. */\nexport const REVOCATION_RECORD_VERSION = 1;\n\n/** Magic header bytes for sanity-checking parsed revocations. ASCII \"PRV1\". */\nexport const REVOCATION_MAGIC = new Uint8Array([0x50, 0x52, 0x56, 0x31]);\n\n/**\n * The payload carried inside a signed revocation envelope.\n */\nexport interface RevocationRecord {\n /** Format version. */\n version: number;\n /** The peer id doing the revoking. Becomes the `senderId` of the signed\n * envelope when the record is serialised. */\n issuerPeerId: string;\n /** The peer id being revoked. After a receiver applies this record, its\n * MeshNetworkAdapter will drop all further messages from this peer. */\n revokedPeerId: string;\n /** Unix timestamp (milliseconds) when the revocation was issued. Carried\n * for audit and for tie-breaking in case an issuer issues multiple\n * revocations targeting the same peer. */\n issuedAt: number;\n /** Optional human-readable reason. Not used by the enforcement layer;\n * applications can surface it in audit logs. */\n reason?: string;\n}\n\n/** Errors thrown by the revocation subsystem. */\nexport class RevocationError extends Error {\n readonly code:\n | \"unknown-issuer\"\n | \"unauthorised-issuer\"\n | \"signature-invalid\"\n | \"truncated\"\n | \"wrong-magic\"\n | \"unknown-version\"\n | \"not-signed-by-issuer\";\n\n constructor(message: string, code: RevocationError[\"code\"]) {\n super(message);\n this.name = \"RevocationError\";\n this.code = code;\n }\n}\n\n/** Options for {@link createRevocation}. */\nexport interface CreateRevocationOptions {\n /** Signing keypair of the issuer. The private key signs the record; the\n * public key is looked up by the receiver via `issuerPeerId`. */\n issuer: SigningKeyPair;\n /** Stable peer id of the issuer. */\n issuerPeerId: string;\n /** Peer id to revoke. */\n revokedPeerId: string;\n /** Optional human-readable reason. */\n reason?: string;\n /** Override the current time. Intended for tests. */\n now?: () => number;\n}\n\n/**\n * Build a fresh {@link RevocationRecord}. The returned record is plain\n * data; use {@link encodeRevocation} to wrap it in a signed envelope\n * suitable for transport.\n */\nexport function createRevocation(options: CreateRevocationOptions): RevocationRecord {\n const now = options.now ? options.now() : Date.now();\n return {\n version: REVOCATION_RECORD_VERSION,\n issuerPeerId: options.issuerPeerId,\n revokedPeerId: options.revokedPeerId,\n issuedAt: now,\n ...(options.reason === undefined ? {} : { reason: options.reason }),\n };\n}\n\n/**\n * Apply a verified revocation to a keyring. Mutates the keyring in place:\n * adds the target peer id to {@link MeshKeyring.revokedPeers}. The caller\n * must verify the revocation (via {@link decodeRevocation}) before\n * applying; this function does not revalidate the signature.\n */\nexport function applyRevocation(record: RevocationRecord, keyring: MeshKeyring): void {\n keyring.revokedPeers.add(record.revokedPeerId);\n}\n\n/**\n * Revoke a peer locally without producing a transportable record. Useful\n * for the common case where an application decides to drop a peer on this\n * device without propagating the decision elsewhere (tests, single-device\n * setups). Peers that want the revocation to spread to other devices\n * should use {@link createRevocation} + {@link encodeRevocation}.\n */\nexport function revokePeerLocally(peerId: string, keyring: MeshKeyring): void {\n keyring.revokedPeers.add(peerId);\n}\n\n// ─── binary serialisation ──────────────────────────────────────────────────\n\n/**\n * Serialise a {@link RevocationRecord} to a binary payload ready for\n * signing. The layout is:\n *\n * [4 bytes: magic \"PRV1\"]\n * [1 byte: version]\n * [4 bytes BE: issuer id byte length]\n * [N bytes: issuer id UTF-8]\n * [4 bytes BE: revoked peer id byte length]\n * [M bytes: revoked peer id UTF-8]\n * [8 bytes BE: issuedAt (uint64 milliseconds)]\n * [4 bytes BE: reason byte length]\n * [R bytes: reason UTF-8]\n */\nfunction serialiseRevocationPayload(record: RevocationRecord): Uint8Array {\n const issuerBytes = new TextEncoder().encode(record.issuerPeerId);\n const revokedBytes = new TextEncoder().encode(record.revokedPeerId);\n const reasonBytes = new TextEncoder().encode(record.reason ?? \"\");\n\n const total =\n REVOCATION_MAGIC.length +\n 1 + // version\n 4 +\n issuerBytes.length +\n 4 +\n revokedBytes.length +\n 8 + // issuedAt\n 4 +\n reasonBytes.length;\n\n const out = new Uint8Array(total);\n let offset = 0;\n\n out.set(REVOCATION_MAGIC, offset);\n offset += REVOCATION_MAGIC.length;\n\n out[offset] = record.version;\n offset += 1;\n\n const view = new DataView(out.buffer);\n view.setUint32(offset, issuerBytes.length, false);\n offset += 4;\n out.set(issuerBytes, offset);\n offset += issuerBytes.length;\n\n view.setUint32(offset, revokedBytes.length, false);\n offset += 4;\n out.set(revokedBytes, offset);\n offset += revokedBytes.length;\n\n view.setBigUint64(offset, BigInt(record.issuedAt), false);\n offset += 8;\n\n view.setUint32(offset, reasonBytes.length, false);\n offset += 4;\n out.set(reasonBytes, offset);\n\n return out;\n}\n\n/** Inverse of {@link serialiseRevocationPayload}. */\nfunction parseRevocationPayload(bytes: Uint8Array): RevocationRecord {\n let offset = 0;\n\n if (bytes.length < REVOCATION_MAGIC.length) {\n throw new RevocationError(\"Revocation record too short for magic.\", \"truncated\");\n }\n for (let i = 0; i < REVOCATION_MAGIC.length; i++) {\n if (bytes[offset + i] !== REVOCATION_MAGIC[i]) {\n throw new RevocationError(\"Revocation record magic mismatch.\", \"wrong-magic\");\n }\n }\n offset += REVOCATION_MAGIC.length;\n\n if (bytes.length < offset + 1) {\n throw new RevocationError(\"Revocation record truncated at version.\", \"truncated\");\n }\n const version = bytes[offset] as unknown as number;\n offset += 1;\n if (version !== REVOCATION_RECORD_VERSION) {\n throw new RevocationError(`Unknown revocation record version: ${version}.`, \"unknown-version\");\n }\n\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\n if (bytes.length < offset + 4) {\n throw new RevocationError(\"Revocation record truncated at issuer length.\", \"truncated\");\n }\n const issuerLen = view.getUint32(offset, false);\n offset += 4;\n if (bytes.length < offset + issuerLen) {\n throw new RevocationError(\"Revocation record truncated at issuer id.\", \"truncated\");\n }\n const issuerPeerId = new TextDecoder().decode(bytes.subarray(offset, offset + issuerLen));\n offset += issuerLen;\n\n if (bytes.length < offset + 4) {\n throw new RevocationError(\"Revocation record truncated at revoked id length.\", \"truncated\");\n }\n const revokedLen = view.getUint32(offset, false);\n offset += 4;\n if (bytes.length < offset + revokedLen) {\n throw new RevocationError(\"Revocation record truncated at revoked id.\", \"truncated\");\n }\n const revokedPeerId = new TextDecoder().decode(bytes.subarray(offset, offset + revokedLen));\n offset += revokedLen;\n\n if (bytes.length < offset + 8) {\n throw new RevocationError(\"Revocation record truncated at issuedAt.\", \"truncated\");\n }\n const issuedAt = Number(view.getBigUint64(offset, false));\n offset += 8;\n\n if (bytes.length < offset + 4) {\n throw new RevocationError(\"Revocation record truncated at reason length.\", \"truncated\");\n }\n const reasonLen = view.getUint32(offset, false);\n offset += 4;\n if (bytes.length < offset + reasonLen) {\n throw new RevocationError(\"Revocation record truncated at reason.\", \"truncated\");\n }\n const reason = new TextDecoder().decode(bytes.subarray(offset, offset + reasonLen));\n offset += reasonLen;\n\n return {\n version,\n issuerPeerId,\n revokedPeerId,\n issuedAt,\n ...(reason ? { reason } : {}),\n };\n}\n\n/**\n * Produce a signed, transportable binary blob from a revocation record.\n * Wraps the serialised payload in a {@link SignedEnvelope} signed by the\n * issuer's keypair, then encodes the envelope to bytes.\n */\nexport function encodeRevocation(record: RevocationRecord, issuer: SigningKeyPair): Uint8Array {\n const payload = serialiseRevocationPayload(record);\n const envelope = signEnvelope(payload, record.issuerPeerId, issuer.secretKey);\n return encodeSignedEnvelope(envelope);\n}\n\n/**\n * Parse and verify a signed revocation blob. Returns the decoded record\n * on success; throws {@link RevocationError} on any failure.\n *\n * Verification requires the keyring to already know the issuer's public\n * key (because Polly does not have a global PKI). If the issuer is\n * unknown, the caller receives `unknown-issuer` and can decide whether\n * to cache the revocation for later verification or drop it outright.\n *\n * Authorisation: when the keyring has a non-empty\n * {@link MeshKeyring.revocationAuthority} set, only issuers inside that\n * set can have their revocations accepted. An issuer outside the set\n * produces `unauthorised-issuer`. When the authority set is undefined\n * or empty, any signed revocation from a known peer is accepted — that\n * is the Phase 2 first-cut default, preserved for backward compatibility.\n */\nexport function decodeRevocation(bytes: Uint8Array, keyring: MeshKeyring): RevocationRecord {\n const envelope = decodeSignedEnvelope(bytes);\n const issuerKey = keyring.knownPeers.get(envelope.senderId);\n if (!issuerKey) {\n throw new RevocationError(\n `Revocation issuer ${envelope.senderId} is not in the local keyring.`,\n \"unknown-issuer\"\n );\n }\n if (\n keyring.revocationAuthority !== undefined &&\n keyring.revocationAuthority.size > 0 &&\n !keyring.revocationAuthority.has(envelope.senderId)\n ) {\n throw new RevocationError(\n `Revocation issuer ${envelope.senderId} is not in the keyring's revocation authority set.`,\n \"unauthorised-issuer\"\n );\n }\n let payloadBytes: Uint8Array;\n try {\n payloadBytes = openSignedEnvelope(envelope, issuerKey);\n } catch {\n throw new RevocationError(\n `Revocation signature failed verification for issuer ${envelope.senderId}.`,\n \"signature-invalid\"\n );\n }\n\n const record = parseRevocationPayload(payloadBytes);\n // Paranoid cross-check: the payload's claimed issuer must match the\n // envelope's authenticated sender. Mismatch means a forged payload.\n if (record.issuerPeerId !== envelope.senderId) {\n throw new RevocationError(\n `Revocation payload claims issuer ${record.issuerPeerId} but the envelope was signed by ${envelope.senderId}.`,\n \"not-signed-by-issuer\"\n );\n }\n return record;\n}\n"
|
|
18
26
|
],
|
|
19
|
-
"mappings": "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",
|
|
20
|
-
"debugId": "
|
|
27
|
+
"mappings": "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",
|
|
28
|
+
"debugId": "EBE34B8E4170302164756E2164756E21",
|
|
21
29
|
"names": []
|
|
22
30
|
}
|