Files
WRNexusJSDoc/app/pages/packages/realtime.wrn
T

280 lines
19 KiB
Plaintext

page wrnexusrealtime {
seo {
title = "@wrnexus/realtime"
description = "Rooms, presence, messaging, history, streams, and UI components."
}
view {
<div class="docs-shell">
<a href="#main" class="skip-link">Skip to content</a>
<header class="topbar">
<a class="brand" href="/"><span>W</span> WRNexusJS</a>
<nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav>
<div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v0.8.8</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme" title="Toggle color theme">◐</button></div>
</header>
<div class="mobile-doc-nav"><details><summary>Browse documentation</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/guides/project-structure">Guides</a><a href="/examples">Examples</a><a href="/search">Search</a></nav></details></div>
<main id="main" class="portal-main docs-layout docs-layout--navigation"><aside class="docs-section-menu" aria-label="Documentation sections"><nav>
<section><strong>Get started</strong><a href="/getting-started">Quick start</a><a href="/tutorial">Tutorial</a><a href="/guides/full-stack-auth-demo">Auth dashboard demo</a><a href="/guides/configuration-and-profiles">Configuration</a></section>
<section><strong>Build</strong><a href="/guides/pages-and-components">Pages and components</a><a href="/guides/routing">Routing</a><a href="/guides/server-data">Server data</a><a href="/guides/forms-and-validation">Forms and validation</a><a href="/guides/database">Database</a></section>
<section><strong>Auth and security</strong><a href="/guides/authentication">Authentication</a><a href="/guides/authorization">Authorization</a><a href="/guides/security">Application security</a><a href="/security">Security policy</a></section>
<section><strong>Reference</strong><a href="/packages">Packages overview</a><a href="/packages/cli">CLI</a><a href="/language">.wrn language</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/examples">Examples</a></section>
<section class="package-menu"><strong>Packages</strong><a href="/packages/ai">@wrnexus/ai</a><a href="/packages/auth">@wrnexus/auth</a><a href="/packages/authz">@wrnexus/authz</a><a href="/packages/benchmark">@wrnexus/benchmark</a><a href="/packages/cache">@wrnexus/cache</a><a href="/packages/captcha">@wrnexus/captcha</a><a href="/packages/cli">@wrnexus/cli</a><a href="/packages/compiler">@wrnexus/compiler</a><a href="/packages/content">@wrnexus/content</a><a href="/packages/core">@wrnexus/core</a><a href="/packages/csr">@wrnexus/csr</a><a href="/packages/db">@wrnexus/db</a><a href="/packages/dev-server">@wrnexus/dev-server</a><a href="/packages/dev-toolbar">@wrnexus/dev-toolbar</a><a href="/packages/encryption">@wrnexus/encryption</a><a href="/packages/graphql">@wrnexus/graphql</a><a href="/packages/helpers">@wrnexus/helpers</a><a href="/packages/i18n">@wrnexus/i18n</a><a href="/packages/identity">@wrnexus/identity</a><a href="/packages/image">@wrnexus/image</a><a href="/packages/jwt">@wrnexus/jwt</a><a href="/packages/language-server">@wrnexus/language-server</a><a href="/packages/mcp">@wrnexus/mcp</a><a href="/packages/mobile">@wrnexus/mobile</a><a href="/packages/native">@wrnexus/native</a><a href="/packages/oauth">@wrnexus/oauth</a><a href="/packages/observability">@wrnexus/observability</a><a href="/packages/playground">@wrnexus/playground</a><a href="/packages/plugin">@wrnexus/plugin</a><a href="/packages/pubsub">@wrnexus/pubsub</a><a href="/packages/pwa">@wrnexus/pwa</a><a href="/packages/queue">@wrnexus/queue</a><a href="/packages/reactive">@wrnexus/reactive</a><a href="/packages/realtime">@wrnexus/realtime</a><a href="/packages/router">@wrnexus/router</a><a href="/packages/security">@wrnexus/security</a><a href="/packages/ssr">@wrnexus/ssr</a><a href="/packages/store">@wrnexus/store</a><a href="/packages/styles">@wrnexus/styles</a><a href="/packages/syntax">@wrnexus/syntax</a><a href="/packages/test">@wrnexus/test</a><a href="/packages/tracking">@wrnexus/tracking</a><a href="/packages/typecheck">@wrnexus/typecheck</a><a href="/packages/ui">@wrnexus/ui</a><a href="/packages/uploader">@wrnexus/uploader</a><a href="/packages/validation">@wrnexus/validation</a></section>
</nav></aside><div class="docs-reading-column">
<article class="documentation prose standalone package-document"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><a href="/packages">Packages</a><span>/</span><span aria-current="page">@wrnexus/realtime</span></nav><section class="doc-intro"><span class="eyebrow">Realtime · Package reference</span><h1>@wrnexus/realtime</h1><p>Rooms, presence, messaging, history, streams, and UI components.</p><div class="doc-meta"><span>v0.8.8</span><span>Private registry</span><span>Realtime</span></div><section id="access" class="access-callout"><h2>Install the package</h2><p>After WorkRoot approves private registry access, install the release-aligned package:</p><pre><code>bun add @wrnexus/realtime@0.8.8</code><button type="button" class="copy-button" aria-label="Copy installation command">Copy</button></pre><p><a href="/access">Request preview access</a>. Never put registry tokens in source control.</p></section></section><section id="guide"><p>Typed rooms, secure message envelopes, browser room helpers, presence utilities, and complete realtime UI blocks for WRNexusJS.</p>
<h3 id="server-rooms">Server rooms</h3>
<pre data-language="ts"><code>import &#123; defineRoom &#125; from &quot;@wrnexus/realtime&quot;;
export default defineRoom(&quot;support&quot;, &#123;
async authorize(context) &#123;
return Boolean(context.user);
&#125;,
message(client, message) &#123;
client.broadcast(message);
&#125;,
&#125;);</code></pre>
<p>The package re-exports the hardened realtime registry from <code>@wrnexus/core</code>, including authentication, origin checks, quotas, message-size limits, schema validation hooks, and room authorization.</p>
<h3 id="messages-and-browser-helpers">Messages and browser helpers</h3>
<pre data-language="ts"><code>import &#123;
createRealtimeMessage,
createPresenceEvent,
createTypingEvent,
connectRoom,
sendRoomMessage,
&#125; from &quot;@wrnexus/realtime&quot;;
const room = connectRoom(&quot;support&quot;, &#123; query: &#123; ticket: &quot;T-100&quot; &#125; &#125;);
sendRoomMessage(
room,
createRealtimeMessage(&#123;
type: &quot;message&quot;,
room: &quot;support&quot;,
data: &#123; text: &quot;Hello&quot; &#125;,
&#125;),
);</code></pre>
<p>Message IDs use Web Crypto. A runtime without secure randomness must provide an explicit message ID.</p>
<h3 id="components">Components</h3>
<p>Enable <code>realtimePlugin()</code> and use:</p>
<ul>
<li><code>&lt;RealtimeRoom /&gt;</code></li>
<li><code>&lt;RealtimeMessageBubble /&gt;</code></li>
<li><code>&lt;MessageComposer /&gt;</code></li>
<li><code>&lt;RoomStatus /&gt;</code></li>
<li><code>&lt;RoomMeta /&gt;</code></li>
<li><code>&lt;PresenceList /&gt;</code></li>
<li><code>&lt;TypingIndicator /&gt;</code></li>
</ul>
<p>These package-owned blocks compose existing <code>@wrnexus/ui</code> components such as <code>Card</code>, <code>Alert</code>, <code>Avatar</code>, <code>Badge</code>, <code>Button</code>, <code>Input</code>, and <code>ChatBubble</code>.</p>
<p>Incoming messages can be bounded and constrained:</p>
<pre data-language="ts"><code>const message = parseRealtimeMessage(rawMessage, &#123;
maxBytes: 64 * 1024,
maxDepth: 12,
allowedTypes: [&quot;message&quot;, &quot;typing&quot;, &quot;presence&quot;],
room: &quot;support&quot;,
&#125;);</code></pre>
<p>The parser rejects oversized payloads, circular/unsupported values, unsafe object keys, invalid message types, invalid room names, and room mismatches.</p>
<h3 id="replay-acknowledgements-sse-and-monitoring">Replay, acknowledgements, SSE, and monitoring</h3>
<p><code>createRealtimeHistory()</code> keeps a bounded sequenced log per room. Clients acknowledge a sequence and <code>resume(room, clientId)</code> returns only missed events. A snapshot exposes room, message, acknowledgement, and sequence counts for monitoring without exposing payloads.</p>
<pre data-language="ts"><code>const history = createRealtimeHistory(&#123; limitPerRoom: 100 &#125;);
const entry = history.publish(&quot;support&quot;, message);
history.acknowledge(&quot;support&quot;, clientId, entry.sequence);
const missed = history.resume(&quot;support&quot;, clientId);</code></pre>
<p><code>realtimeSseResponse(stream, signal)</code> converts the same sequenced envelope into standards-based Server-Sent Events with event IDs, event types, JSON data, cancellation, and no-cache headers.</p></section><section id="api" class="api"><h2>Complete TypeScript API</h2><p>Generated from the exact installed package declarations.</p><pre data-language="typescript"><code>export &#123; RawSocket, RealtimeBridge, RealtimeBus, RealtimeConnectMeta, RealtimeEnvelope, RealtimeHandler, RealtimeRegistry, RealtimeRegistryOptions, RealtimeSecurityOptions, RealtimeSocket, Room, RoomAuthInfo, RoomClient, RoomDefinition, RoomHandlers, Target, bridgeRealtime, createRealtimeRegistry, defineRoom, isRoomDefinition &#125; from '@wrnexus/core';
export &#123; RealtimePluginOptions, realtimeComponentsDir, default as realtimePlugin &#125; from './plugin.js';
import '@wrnexus/plugin';
type RealtimeMessageType = string;
interface RealtimeMessage&lt;T = unknown&gt; &#123;
id: string;
type: RealtimeMessageType;
room?: string;
senderId?: string;
senderName?: string;
sentAt: string;
data: T;
meta?: Record&lt;string, string | number | boolean | null&gt;;
&#125;
interface CreateRealtimeMessageOptions&lt;T&gt; &#123;
id?: string;
type: string;
room?: string;
senderId?: string;
senderName?: string;
sentAt?: string | Date;
data: T;
meta?: Record&lt;string, string | number | boolean | null&gt;;
&#125;
interface ParseRealtimeMessageOptions &#123;
maxBytes?: number;
maxDepth?: number;
allowedTypes?: readonly string[];
room?: string;
&#125;
interface RealtimePresence &#123;
userId: string;
name?: string;
avatar?: string;
status?: &quot;online&quot; | &quot;away&quot; | &quot;busy&quot; | &quot;offline&quot;;
joinedAt?: string;
lastSeenAt?: string;
meta?: Record&lt;string, string | number | boolean | null&gt;;
&#125;
interface RealtimeRoomMeta &#123;
id: string;
name: string;
description?: string;
memberCount?: number;
onlineCount?: number;
createdAt?: string;
updatedAt?: string;
private?: boolean;
tags?: string[];
&#125;
declare function assertRealtimeRoomName(name: string): string;
declare function createRealtimeMessage&lt;T&gt;(options: CreateRealtimeMessageOptions&lt;T&gt;): RealtimeMessage&lt;T&gt;;
declare function isRealtimeMessage(value: unknown): value is RealtimeMessage;
declare function parseRealtimeMessage&lt;T = unknown&gt;(value: string | unknown, options?: ParseRealtimeMessageOptions): RealtimeMessage&lt;T&gt;;
declare function createPresenceEvent(action: &quot;join&quot; | &quot;leave&quot; | &quot;update&quot;, presence: RealtimePresence, room?: string): RealtimeMessage&lt;&#123;
action: &quot;join&quot; | &quot;leave&quot; | &quot;update&quot;;
presence: RealtimePresence;
&#125;&gt;;
declare function createTypingEvent(userId: string, typing: boolean, options?: &#123;
room?: string;
name?: string;
&#125;): RealtimeMessage&lt;&#123;
userId: string;
name?: string;
typing: boolean;
&#125;&gt;;
declare function roomMemberSummary(members: readonly RealtimePresence[]): &#123;
total: number;
online: number;
away: number;
busy: number;
&#125;;
interface BrowserRoomConnection &#123;
readonly name: string;
send(message: unknown): BrowserRoomConnection;
on(type: string | ((message: unknown) =&gt; void), callback?: (message: unknown) =&gt; void): BrowserRoomConnection;
close(): void;
&#125;
interface WrnexusRealtimeWindow extends Window &#123;
wire?: &#123;
room?: (name: string, query?: string) =&gt; BrowserRoomConnection;
&#125;;
&#125;
declare function roomQuery(params: Record&lt;string, string | number | boolean | null | undefined&gt;): string;
declare function connectRoom(name: string, options?: &#123;
query?: Record&lt;string, string | number | boolean | null | undefined&gt;;
window?: WrnexusRealtimeWindow;
&#125;): BrowserRoomConnection;
declare function sendRoomMessage&lt;T&gt;(room: BrowserRoomConnection, message: RealtimeMessage&lt;T&gt; | T): BrowserRoomConnection;
interface SequencedRealtimeMessage&lt;T = unknown&gt; &#123;
sequence: number;
message: RealtimeMessage&lt;T&gt;;
&#125;
interface RealtimeHistorySnapshot &#123;
rooms: number;
messages: number;
acknowledgements: number;
oldestSequence?: number;
latestSequence?: number;
&#125;
interface RealtimeHistoryOptions &#123;
limitPerRoom?: number;
maxClients?: number;
&#125;
interface RealtimeHistory &#123;
publish&lt;T&gt;(room: string, message: RealtimeMessage&lt;T&gt;): SequencedRealtimeMessage&lt;T&gt;;
replay(room: string, afterSequence?: number, limit?: number): SequencedRealtimeMessage[];
acknowledge(room: string, clientId: string, sequence: number): void;
acknowledged(room: string, clientId: string): number;
resume(room: string, clientId: string, limit?: number): SequencedRealtimeMessage[];
snapshot(): RealtimeHistorySnapshot;
clear(room?: string): void;
&#125;
declare function createRealtimeHistory(options?: RealtimeHistoryOptions): RealtimeHistory;
declare function createAcknowledgement(room: string, sequence: number, clientId: string): RealtimeMessage&lt;&#123;
sequence: number;
clientId: string;
&#125;&gt;;
declare function realtimeSseResponse(stream: ReadableStream&lt;SequencedRealtimeMessage&gt;, signal?: AbortSignal): Response;
interface DatabaseChange&lt;T = unknown&gt; &#123;
table: string;
operation: &quot;insert&quot; | &quot;update&quot; | &quot;delete&quot;;
key?: string | number;
record?: T;
occurredAt: number;
&#125;
interface DatabaseChangeSource &#123;
subscribe(handler: (change: DatabaseChange) =&gt; void | Promise&lt;void&gt;): () =&gt; void;
&#125;
declare function databaseChangeFeed(source: DatabaseChangeSource, publish: (topic: string, change: DatabaseChange) =&gt; void | Promise&lt;void&gt;, options?: &#123;
prefix?: string;
allowTables?: string[];
&#125;): () =&gt; void;
interface FileStreamFrame &#123;
streamId: string;
index: number;
total: number;
bytes: Uint8Array;
&#125;
declare function frameFileStream(streamId: string, bytes: Uint8Array, options?: &#123;
chunkBytes?: number;
maxBytes?: number;
&#125;): FileStreamFrame[];
declare function createFileStreamReceiver(options?: &#123;
maxBytes?: number;
maxStreams?: number;
&#125;): &#123;
accept(frame: FileStreamFrame): Uint8Array | null;
snapshot: () =&gt; &#123;
activeStreams: number;
bufferedBytes: number;
&#125;;
&#125;;
export &#123; type BrowserRoomConnection, type CreateRealtimeMessageOptions, type DatabaseChange, type DatabaseChangeSource, type FileStreamFrame, type ParseRealtimeMessageOptions, type RealtimeHistory, type RealtimeHistoryOptions, type RealtimeHistorySnapshot, type RealtimeMessage, type RealtimeMessageType, type RealtimePresence, type RealtimeRoomMeta, type SequencedRealtimeMessage, type WrnexusRealtimeWindow, assertRealtimeRoomName, connectRoom, createAcknowledgement, createFileStreamReceiver, createPresenceEvent, createRealtimeHistory, createRealtimeMessage, createTypingEvent, databaseChangeFeed, frameFileStream, isRealtimeMessage, parseRealtimeMessage, realtimeSseResponse, roomMemberSummary, roomQuery, sendRoomMessage &#125;;
</code></pre></section><section id="examples" class="examples"><h2>Examples</h2><p>Copy-ready examples from the installed package documentation.</p><div class="example-grid"><article class="example-card"><h3>## Server rooms</h3><pre data-language="ts"><code>import &#123; defineRoom &#125; from &quot;@wrnexus/realtime&quot;;
export default defineRoom(&quot;support&quot;, &#123;
async authorize(context) &#123;
return Boolean(context.user);
&#125;,
message(client, message) &#123;
client.broadcast(message);
&#125;,
&#125;);</code></pre></article><article class="example-card"><h3>## Messages and browser helpers</h3><pre data-language="ts"><code>import &#123;
createRealtimeMessage,
createPresenceEvent,
createTypingEvent,
connectRoom,
sendRoomMessage,
&#125; from &quot;@wrnexus/realtime&quot;;
const room = connectRoom(&quot;support&quot;, &#123; query: &#123; ticket: &quot;T-100&quot; &#125; &#125;);
sendRoomMessage(
room,
createRealtimeMessage(&#123;
type: &quot;message&quot;,
room: &quot;support&quot;,
data: &#123; text: &quot;Hello&quot; &#125;,
&#125;),
);</code></pre></article><article class="example-card"><h3>Incoming messages can be bounded and constrained</h3><pre data-language="ts"><code>const message = parseRealtimeMessage(rawMessage, &#123;
maxBytes: 64 * 1024,
maxDepth: 12,
allowedTypes: [&quot;message&quot;, &quot;typing&quot;, &quot;presence&quot;],
room: &quot;support&quot;,
&#125;);</code></pre></article><article class="example-card"><h3>message, acknowledgement, and sequence counts for monitoring without exposing payloads.</h3><pre data-language="ts"><code>const history = createRealtimeHistory(&#123; limitPerRoom: 100 &#125;);
const entry = history.publish(&quot;support&quot;, message);
history.acknowledge(&quot;support&quot;, clientId, entry.sequence);
const missed = history.resume(&quot;support&quot;, clientId);</code></pre></article></div></section></article></div>
<aside class="on-this-page"><h2>On this page</h2><nav><a class="toc-level-2" href="#guide">Guide</a><a class="toc-level-3" href="#server-rooms">Server rooms</a><a class="toc-level-3" href="#messages-and-browser-helpers">Messages and browser helpers</a><a class="toc-level-3" href="#components">Components</a><a class="toc-level-3" href="#replay-acknowledgements-sse-and-monitoring">Replay, acknowledgements, SSE, and monitoring</a><a class="toc-level-2" href="#api">Complete API</a><a class="toc-level-2" href="#examples">Examples</a></nav></aside>
</main>
<footer><div class="footer-brand"><span class="footer-mark" aria-hidden="true">W</span><p><strong>WRNexusJS 0.8.8</strong><span>Complete API documentation generated from installed package declarations.</span></p></div><nav aria-label="Footer"><a href="/packages">All packages</a><a href="/getting-started">Get started</a><a href="/security">Security</a><a href="/support">Support</a><a href="/llms.txt">AI guide</a></nav><p class="footer-meta">Private Developer Preview · Bun-native</p></footer>
</div>
}
}