# Data — Cookbook

> Short, copy-pasteable how-to snippets for the data modality: publish, subscribe with + / # filters, retained state, lanes, fan-in, and JSON.

Small, task-focused snippets for the data modality. Each assumes a constructed
client:

```ts
import { Data } from "@clutchcall/sdk/data";

const data = new Data({
  token:    process.env.CLUTCHCALL_CREDENTIALS!,
  clientId: "device-7",
});
```

## Publish a reading (lossy, default)

The default lane is the QUIC datagram lane — lowest latency, best for high-rate
telemetry where the freshest value wins.

```ts
await data.publish({
  topic:   "sensors/room1/temperature",
  payload: new TextEncoder().encode("23.5"),
});
```

## Publish an event that must arrive (reliable lane)

Flip `reliable: true` for application events, commands, or settlements — ordered
and retried per publisher.

```ts
await data.publish({
  topic:    "events/orders/shipped",
  payload:  new TextEncoder().encode(JSON.stringify({ orderId: "A-91" })),
  reliable: true,
});
```

## Subscribe with a `+` (single-segment) wildcard

`+` matches exactly one path segment — here, any room.

```ts
const sub = await data.subscribe(
  { topicFilter: "sensors/+/temperature" },
  (msg) => console.log(msg.topic, new TextDecoder().decode(msg.payload)),
);
```

## Subscribe to a whole subtree with `#`

`#` matches the rest of the path and must be the trailing segment. One
subscription covers everything under `events/`.

```ts
await data.subscribe({ topicFilter: "events/#" }, (msg) => {
  console.log("event:", msg.topic, "from", msg.fromClientId);
});
```

## Publish retained current state

A retained message is cached by the relay and delivered to every future
subscriber on attach. Use it for "current state."

```ts
await data.publish({
  topic:    "devices/device-7/state",
  payload:  new TextEncoder().encode(JSON.stringify({ online: true, version: "1.4.2" })),
  retained: true,
});
```

## Clear a retained topic

Publish a zero-length payload with `retained: true` — typically on clean
shutdown so dashboards flip to "offline" without a heartbeat timeout.

```ts
await data.publish({
  topic:    "devices/device-7/state",
  payload:  new Uint8Array(0),
  retained: true,
});
```

## Tell a bootstrap value from a live one

Retained values re-emitted on attach arrive with `msg.retained === true`. Render
the snapshot, then track live updates.

```ts
await data.subscribe({ topicFilter: "devices/+/state" }, (msg) => {
  if (msg.retained) applySnapshot(msg);   // cached current state
  else               applyUpdate(msg);    // live change
});
```

## Fan in from a whole fleet to one dashboard

Every message carries `fromClientId`, so one subscription demuxes many
publishers without a track per device.

```ts
const latest = new Map<string, number>();
await data.subscribe({ topicFilter: "sensors/+/temperature" }, (msg) => {
  latest.set(msg.fromClientId, Number(new TextDecoder().decode(msg.payload)));
});
```

## Publish and consume JSON

Payloads are opaque bytes — encode and decode JSON yourself.

```ts
const enc = (o: unknown) => new TextEncoder().encode(JSON.stringify(o));
const dec = (b: Uint8Array) => JSON.parse(new TextDecoder().decode(b));

await data.publish({ topic: "events/users/signup", payload: enc({ userId: "u-7" }) });

await data.subscribe({ topicFilter: "events/users/#" }, (msg) => {
  const body = dec(msg.payload);
  console.log("signup", body.userId);
});
```

## Route by topic with a dispatch table

One subscription on a subtree, many handlers keyed by sub-filter — the classic
event-bus shape.

```ts
import { topicMatches } from "@clutchcall/sdk/data";

const routes: [string, (m: any) => void][] = [
  ["events/orders/shipped", (m) => onShipped(m)],
  ["events/users/signup",   (m) => onSignup(m)],
];

await data.subscribe({ topicFilter: "events/#" }, (msg) => {
  for (const [pattern, fn] of routes)
    if (topicMatches(msg.topic, pattern)) return fn(msg);
});
```

## Stop a single subscription

```ts
const sub = await data.subscribe({ topicFilter: "sensors/#" }, handle);
// later…
sub.close();
```

## Watch connection state

Pass `onState` to react to reconnects. Subscriptions and retained values are
replayed automatically after a drop.

```ts
const data = new Data({
  token, clientId: "device-7",
  onState: (state, reason) => console.log("data session:", state, reason ?? ""),
});
```

## Publish from Python

The Python surface mirrors the TS one with `snake_case` keywords.

```python
from clutchcall.data import Data

data = Data(token=token, client_id="device-7")
data.publish(topic="sensors/room1/temperature", payload=b"23.5")
sub = data.subscribe(
    topic_filter="sensors/+/temperature",
    on_message=lambda m: print(m.topic, m.from_client_id, m.payload),
)
```

## Stay wire-compatible without the client

Use the exported codec to build or parse frames yourself.

```ts
import { encodeDataFrame, decodeDataFrame } from "@clutchcall/sdk/data";

const frame   = encodeDataFrame("device-7", "sensors/room1/temp", payload);
const decoded = decodeDataFrame(frame);   // { fromClientId, topic, payload }
```

> **TIP:**
> Keep the top-level topic segment concrete and meaningful (`sensors`, `events`,
> `devices`) — it selects the underlying track, so it is also your coarse fan-out
> and routing boundary.
