# TypeScript SDK

The TypeScript SDK provides a browser-compatible client with WASM-based crypto.

Location: `sdks/typescript/`

## Installation

```sh
npm install quicproquo
```

## Quick Start

```typescript
import { QpqClient } from "quicproquo";

// Connect via WebSocket bridge
const client = await QpqClient.connect({
  addr: "ws://127.0.0.1:5002",
});

const status = await client.health();
console.log("Server:", status);

// Set session token (obtained via OPAQUE externally)
client.setSessionToken(token);

// Resolve user
const peerKey = await client.resolveUser("bob");

// Create channel
const { channelId, wasNew } = await client.createChannel(peerKey);

// Send message
const seq = await client.send(peerKey, new TextEncoder().encode("hello"));

// Receive messages
const messages = await client.receive(myKey);
for (const msg of messages) {
  console.log(`[${msg.seq}] ${new TextDecoder().decode(msg.data)}`);
}

client.close();
```

## Offline Crypto

The SDK includes WASM-compiled crypto primitives that work without a server:

```typescript
const client = await QpqClient.offline();

// Ed25519 identity
const { seed, publicKey } = client.generateIdentity();
const sig = client.sign(seed, message);
const valid = client.verify(publicKey, message, sig);

// Hybrid KEM (X25519 + ML-KEM-768)
const kp = client.hybridGenerateKeypair();
const ciphertext = client.hybridEncrypt(kp.publicKey, plaintext);
const decrypted = client.hybridDecrypt(kp.blob, ciphertext);

// Safety numbers
const safetyNumber = client.computeSafetyNumber(keyA, keyB);

// Sealed sender
const sealed = client.seal(seed, payload);
const unsealed = client.unseal(sealed);

// Message padding
const padded = client.padMessage(data);
const unpadded = client.unpadMessage(padded);
```

## API

### RPC Methods

| Method | Description |
|---|---|
| `QpqClient.connect(opts)` | Connect via WebSocket |
| `QpqClient.offline()` | Crypto-only mode |
| `client.health()` | Health check |
| `client.resolveUser(username)` | Look up identity key |
| `client.createChannel(peerKey)` | Create DM channel |
| `client.send(recipientKey, payload)` | Send message |
| `client.sendWithTTL(recipientKey, payload, ttl)` | Disappearing message |
| `client.receive(recipientKey)` | Fetch messages |
| `client.deleteAccount()` | Delete account |
| `client.close()` | Disconnect |

### Crypto Methods

| Method | Description |
|---|---|
| `client.generateIdentity()` | Generate Ed25519 keypair |
| `client.identityPublicKey(seed)` | Derive public key |
| `client.sign(seed, message)` | Ed25519 sign |
| `client.verify(publicKey, message, sig)` | Ed25519 verify |
| `client.hybridGenerateKeypair()` | Generate hybrid keypair |
| `client.hybridEncrypt(pk, plaintext)` | Hybrid encrypt |
| `client.hybridDecrypt(blob, ciphertext)` | Hybrid decrypt |
| `client.computeSafetyNumber(keyA, keyB)` | Safety number |
| `client.seal(seed, payload)` | Sealed sender |
| `client.unseal(envelope)` | Unseal |
| `client.padMessage(data)` | Pad message |
| `client.unpadMessage(data)` | Unpad message |

## Architecture

The TypeScript SDK uses a WebSocket bridge proxy because browsers cannot open raw QUIC connections. The bridge translates JSON-over-WebSocket to Cap'n Proto RPC:

```
Browser  ─── WebSocket ───►  Bridge Proxy  ─── QUIC/capnp ───►  Server
```

Crypto operations (Ed25519, hybrid KEM, sealed sender) run entirely in WASM, compiled from the Rust `quicproquo-core` crate.

## Structure

| File | Purpose |
|---|---|
| `src/client.ts` | High-level client API |
| `src/transport.ts` | WebSocket transport with auto-reconnect |
| `src/crypto.ts` | WASM crypto bindings |
| `src/types.ts` | TypeScript interfaces |
| `wasm-crypto/` | Rust WASM crypto source |
