/** * Trellis Server — Realtime Subscription Engine * * InstantDB-style reactive queries over WebSocket. * * Clients subscribe to an EQL-S query string. When any op lands that touches * an entity type in that query's result set, the server re-runs the query * and pushes the full updated result (plus a diff) to subscribed clients. * * Protocol (JSON over WebSocket): * * Client → Server: * { type: "subscribe", id: "sub_1", query: "find Post where ...", * entityType?: "Post", resolve?: { author: true }, tenantId?: "t1" } * { type: "unsubscribe", id: "sub_1" } * { type: "ping" } * * Server → Client: * { type: "subscribed", id: "sub_1" } * { type: "data", id: "sub_1", result: [...], diff: { added, updated, removed }, * resolved?: true } * { type: "error", id: "sub_1", message: "..." } * { type: "pong" } * * @module trellis/server */ import type { ResolveSpec } from '../schema/resolve.js'; import type { TenantPool } from './tenancy.js'; import type { UsageMeter } from './usage-meter.js'; import type { AuthContext } from './auth.js'; import type { PermissionRegistry } from './permissions.js'; export interface Subscription { id: string; query: string; tenantId: string | null; auth: AuthContext; lastResult: Record[]; /** Entity type name — enables server-side `resolve` expansion. */ entityType?: string; resolve?: ResolveSpec; } export interface WsClient { id: string; ws: { send(data: string): void; readyState: number; }; subscriptions: Map; auth: AuthContext; tenantId: string | null; } export type RealtimeMessage = { type: 'subscribe'; id: string; query: string; tenantId?: string; entityType?: string; resolve?: ResolveSpec; } | { type: 'unsubscribe'; id: string; } | { type: 'ping'; }; /** * Manages WebSocket clients and their query subscriptions. * Call `notify(tenantId)` after any mutation to fan out updates. */ export declare class SubscriptionManager { private clients; private pool; private permissions; private meter; constructor(pool: TenantPool, permissions?: PermissionRegistry | null, meter?: UsageMeter | null); addClient(clientId: string, ws: WsClient['ws'], auth: AuthContext, tenantId: string | null): void; removeClient(clientId: string): void; handleMessage(clientId: string, raw: string): Promise; /** * Re-evaluate all subscriptions for a given tenant and push diffs. * Called after every write op lands. */ notify(tenantId: string | null): Promise; get clientCount(): number; private _handleSubscribe; private _pushUpdate; private _recordGraphIo; private _send; } //# sourceMappingURL=realtime.d.ts.map