# @svelte-lean/query/live

> live() and rule(): socket events that invalidate or patch cached entries; the LiveSource contract.

Page: https://svelte-lean.vercel.app/query/api/live · Markdown: https://svelte-lean.vercel.app/query/api/live.md · Index: https://svelte-lean.vercel.app/llms.txt

live() and rule(): socket events that invalidate cached entries or patch them with patch(). Nothing subscribes until an entry a rule targets is observed.

> **Note** `./live` is experimental: the API can still change in a minor release. Stability per entry point: [Entry points](https://svelte-lean.vercel.app/query.md#entry-points).

## Import

`import`

```ts
import { live, rule } from '@svelte-lean/query/live';
```

## Usage

`src/lib/api/live.ts`

```ts
// src/lib/api/live.ts
import { patch, tag, type } from '@svelte-lean/query';
import { live, rule, type LiveSource } from '@svelte-lean/query/live';
import { getMember } from '$api/members';
import type { Member } from '$api/types';

// An adapter: one EventSource per channel.
const source: LiveSource = {
	subscribe({ channel, event }, deliver, ready) {
		const events = new EventSource(`/events/${channel}`);
		events.addEventListener('open', ready, { once: true });
		events.addEventListener(event, (e) => deliver(JSON.parse((e as MessageEvent).data)));
		return () => events.close();
	}
};

export const realtime = live(source, [
	// Poke, then pull: the observed member lists refetch, coalesced.
	rule('Members', { channel: 'members', event: 'MemberCreated' }),
	// The event carries the row: every cached copy of it is patched, nothing is refetched.
	rule('Members', {
		channel: 'members',
		event: 'MemberUpdated',
		message: type<{ member: Member }>(),
		patch: (m) => patch(tag('Member', m.member.id), () => m.member)
	}),
	// Patch the one detail entry the message names.
	rule(getMember, {
		channel: 'members',
		event: 'MemberPinned',
		message: type<{ id: number; pinned: boolean }>(),
		patch: (m) => patch([getMember, { id: m.id }], (member) => ({ ...member, pinned: m.pinned }))
	})
]);
```

## Size

Production Vite builds of a small consumer entry, Svelte external. A marginal row is the bytes the entry adds over the row it names.

| Build | Over | gzip | brotli | Budget (gzip) |
| --- | --- | --- | --- | --- |
| `live (marginal)` | `query` | 2777 B | 2565 B | 3100 B |

Read from `packages/query/artifacts/size.json`. Every entry and its method: [Performance](https://svelte-lean.vercel.app/query/performance.md).

## Functions

The module and its rules.

### Exports

Runtime exports of the entry.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `live` | `(source: LiveSource, rules: LiveRule[], options?: LiveOptions) => Module` | – | The realtime module; channels are reference-counted and reseeded on reconnect. Inert on the server. |
| # `rule` | `(target: TagTarget, init: LiveRuleInit) => LiveRule` | – | A rule on a tag: every event invalidates it (poke, then pull), or patches the entity it carries. |
| # `rule (endpoint)` | `(target: QueryEndpoint \| [QueryEndpoint, Partial<P>], init: LiveRuleInit & { match? }) => LiveRule` | – | A rule on an endpoint: match narrows its default invalidation to the entries a message concerns. |

## Options

The module options and the rule options.

### LiveOptions

The third argument of live().

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `window` | `Duration` | `'100ms'` | Coalescing window per burst. |
| # `maxWindow` | `Duration` | `'2s'` | Adaptive ceiling of the window under sustained bursts. |
| # `linger` | `Duration` | `'1.5s'` | Keep a channel after its last observer left. |

### LiveRuleInit

The second argument of rule(). match applies to endpoint targets only.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `channel` required | `string \| ((params) => string)` | – | A channel name, or one per entry from its params. |
| # `event` required | `string` | – | The event name. |
| # `message` | `StandardSchemaV1<unknown, M>` | – | Validates each payload; type\<M\>() for no validation. A failure drops the event. |
| # `access` | `Access<P>` | – | The rule's own access; endpoint targets also need the endpoint's verdict. |
| # `when` | `boolean \| (() => boolean)` | – | Reactive gate (session, settings). |
| # `invalidate` | `InvalidateTarget[] \| ((message) => InvalidateTarget[])` | – | What an event invalidates; default the rule's target (narrowed by match) without patch, nothing with it. |
| # `patch` | `(message: M, at: { channel, event }) => void` | – | Applies the event to cached values: call patch() here (the entity it carries, or the entry it concerns). Local writes, also applied when another tab forwards the event. |
| # `reseed` | `boolean` | `true` | Invalidate on a confirmed subscription and on reconnect. |
| # `reason` | `Reason` | – | Reason carried by the refetches this rule starts. |
| # `name` | `string` | `'channel:event'` | Source label of those refetches. |
| # `match` | `(message: M) => Partial<P> \| undefined` | – | The params of the entries a message concerns; the default invalidation reaches only those. |

## Source

Where events come from: a Pusher or Reverb client, an EventSource, a test double (fakeSource() in ./testing).

### LiveSource

The contract an adapter implements.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `subscribe` required | `(at: { channel, event }, deliver, ready) => () => void` | – | Start delivering an event; call ready once the server confirmed; return the stop function. |
| # `watch` | `(listener: (connected: boolean) => void) => () => void` | – | Optional connection state; true after false after true is a reconnect. |

## Guide

How to use it, with examples: [Realtime](https://svelte-lean.vercel.app/query/realtime.md). All entry points: [Svelte Lean Query](https://svelte-lean.vercel.app/query.md).
