/** * Shared wire protocol for `@reactor-team/queue`. * * Both the PartyKit server (`@reactor-team/queue/server`) and the browser * client (`@reactor-team/queue`) import these types so the messages they * exchange over the WebSocket stay in lockstep. Nothing here depends on * PartyKit, the Reactor SDK, React, or the DOM — it is plain data. */ /** Current protocol version. Bumped only on breaking wire changes. */ declare const PROTOCOL_VERSION: 2; /** Default PartyKit room id. A single room is the source of truth for one queue. */ declare const DEFAULT_ROOM = "reactor-queue"; /** Query-string key used to carry the stable per-browser id on connect. */ declare const CLIENT_ID_QUERY_KEY = "rqClientId"; /** Set to `1` on the WebSocket URL to open an admin connection (not queued). */ declare const ADMIN_MODE_QUERY_KEY = "rqAdmin"; /** * Default tunables. Every one of these is overridable from server config and/or * environment variables (see `@reactor-team/queue/server`). */ declare const DEFAULTS: { /** Max concurrent Reactor sessions (GPU ceiling). */ readonly maxSessions: 1; /** Members per session (default 1 = today's behavior; >1 when platform allows N). */ readonly usersPerSession: 1; /** Full session budget once a user has `claim()`ed their slot. */ readonly sessionDurationMs: 120000; /** * Grace window an admitted user gets to actually start (claim) their session * before the slot is reclaimed. Prevents an idle admit from wasting a slot. */ readonly admissionGraceMs: 45000; /** How long before expiry to emit a `time_warning`. */ readonly warningBeforeMs: 30000; /** Lifetime requested for each minted Reactor JWT. Deliberately short. */ readonly tokenTtlSeconds: 60; /** * How often the server re-checks tracked live sessions against the Reactor * API to catch sessions that ended without a clean `session_ended`/close. */ readonly pollIntervalMs: 15000; /** Client-side skew: refresh the JWT this long before it actually expires. */ readonly tokenSkewMs: 10000; }; /** Reactor session states that mean "the slot is free again". */ declare const TERMINAL_SESSION_STATES: readonly ["CLOSED", "INACTIVE"]; /** You are waiting in line. `position` is 1-based. */ interface QueuePositionMessage { type: "queue_position"; position: number; total: number; active: number; capacity: number; } /** * You reached the front and a capacity slot is reserved for you. No Reactor * session exists yet — the server creates it only when you `claim()`, so an * abandoned grace never leaves an orphaned GPU session. You have until the * admission grace expires to `claim()`. */ interface AdmittedMessage { type: "admitted"; active: number; /** Total live users = maxSessions * usersPerSession. */ capacity: number; /** ms the client has to `claim()` before the slot is reclaimed. */ graceMs: number; /** Full session budget (ms) the client receives once it `claim()`s. For countdown UI. */ sessionDurationMs: number; } /** * Sent after `claim()`: the server has created (or reused) the Reactor session * and minted a WebRTC connection under it for this member. Attach with * `connect({ sessionId, connectionId })` — the server owns both, so the client * never creates or stops anything. */ interface SessionReadyMessage { type: "session_ready"; /** Reactor session id created by the server — pass to connect({ sessionId }). */ sessionId: string; /** * Server-minted WebRTC connection id for this member — pass to * connect({ connectionId }). The server registered it under `sessionId`, so * the client adopts it instead of registering its own. */ connectionId: number; /** Full session budget (ms). */ sessionDurationMs: number; /** Unix epoch ms when the session ends. */ expiresAt: number; } /** A freshly minted, short-lived Reactor JWT. Sent on admission and on each `request_token`. */ interface TokenMessage { type: "token"; jwt: string; /** Unix epoch seconds at which the JWT expires. */ expiresAt: number; } /** Your session is about to end. */ interface TimeWarningMessage { type: "time_warning"; secondsLeft: number; /** Unix epoch ms when the session ends. */ expiresAt: number; } /** Your session ended (time ran out, or the server reclaimed the slot). */ interface ExpiredMessage { type: "expired"; reason: "timeout" | "grace_timeout" | "server"; } /** You were refused entry. */ interface RejectedMessage { type: "rejected"; reason: "already_connected" | "server_error" | "forbidden_origin" | string; } /** A non-fatal error (e.g. token mint failed); the client may retry. */ interface ErrorMessage { type: "error"; message: string; } type ServerMessage = QueuePositionMessage | AdmittedMessage | SessionReadyMessage | TokenMessage | TimeWarningMessage | ExpiredMessage | RejectedMessage | ErrorMessage; /** "I'm actually entering the demo" — upgrades the grace window to the full session. */ interface ClaimMessage { type: "claim"; } /** Ask for a fresh JWT. The server only answers if you currently hold a slot. */ interface RequestTokenMessage { type: "request_token"; } /** The user ended the Reactor session from the client; free the slot now. */ interface SessionEndedMessage { type: "session_ended"; } /** Leave the queue / release the slot without intending to rejoin. */ interface LeaveMessage { type: "leave"; } type ClientMessage = ClaimMessage | RequestTokenMessage | SessionEndedMessage | LeaveMessage; /** Read-only server tunables included in every admin snapshot. */ interface AdminConfigSnapshot { maxSessions: number; usersPerSession: number; capacity: number; model: string; webrtcVersion: string; sessionDurationMs: number; admissionGraceMs: number; warningBeforeMs: number; tokenTtlSeconds: number; pollIntervalMs: number; coordinatorUrl: string; apiVersion: number; stopSessionsOnExpiry: boolean; allowDuplicateConnections: boolean; /** "default" = queue creates/stops sessions; "custom" = acquire/release overridden. */ sessionSource: "default" | "custom"; } /** One person waiting in the FIFO queue. */ interface AdminQueuedUserSnapshot { connId: string; /** 1-based position in line. */ position: number; clientId: string | null; } /** One admitted member (may or may not have claimed yet). */ interface AdminMemberSnapshot { connId: string; /** Reactor session id once claimed; null while still in grace (no session yet). */ sessionId: string | null; /** Server-minted WebRTC connection id once claimed; null while still in grace. */ connectionId: number | null; clientId: string | null; claimed: boolean; expiresAt: number; msLeft: number; } /** One capacity slot and its member connection ids. */ interface AdminSessionSnapshot { /** Reactor session id, or null while the slot is reserved but unclaimed (no GPU session yet). */ sessionId: string | null; members: string[]; createdAt: number; msSinceCreated: number; } /** Full room state pushed to authenticated admin connections. */ interface AdminSnapshotMessage { type: "admin_snapshot"; at: number; activeCount: number; sessionCount: number; config: AdminConfigSnapshot; queue: AdminQueuedUserSnapshot[]; sessions: AdminSessionSnapshot[]; members: AdminMemberSnapshot[]; } /** Admin WebSocket authenticated; snapshots follow on changes. */ interface AdminReadyMessage { type: "admin_ready"; } interface AdminRejectedMessage { type: "admin_rejected"; reason: "admin_disabled" | "invalid_password" | "auth_required" | "forbidden_origin"; } interface AdminActionResultMessage { type: "admin_action_result"; action: "kick_member" | "kick_queued" | "close_session"; ok: boolean; message?: string; } /** Severity of an {@link AdminLogEntry}. Mirrors `console.log`/`warn`/`error`. */ type AdminLogLevel = "info" | "warn" | "error"; /** * One structured server event. The queue server emits these for every notable * thing that happens in a room — a user joining, an admission, a session being * created or closed, and crucially the **reason an API call failed** (e.g. a * Coordinator quota rejection, with its HTTP status and body in `data`). They * are streamed live to admins and kept in a bounded server-side ring buffer so * a freshly-connected admin sees recent history. */ interface AdminLogEntry { /** Stable unique id (also usable as a React key). */ id: string; /** Unix epoch ms when the event happened. */ at: number; level: AdminLogLevel; /** Machine-readable event code, e.g. `"user_admitted"`, `"session_create_failed"`. */ event: string; /** Human-readable, already-formatted summary line. */ message: string; /** The connection this event concerns, when applicable. */ connId?: string; /** The Reactor session this event concerns, when applicable. */ sessionId?: string; /** Extra structured context (HTTP status, response body, reason, …). */ data?: Record; } /** A single new log line, pushed live to authenticated admins as it happens. */ interface AdminLogMessage { type: "admin_log"; entry: AdminLogEntry; } /** Recent log history (oldest → newest), sent once right after admin auth. */ interface AdminLogHistoryMessage { type: "admin_log_history"; entries: AdminLogEntry[]; } type AdminServerMessage = AdminReadyMessage | AdminRejectedMessage | AdminSnapshotMessage | AdminActionResultMessage | AdminLogMessage | AdminLogHistoryMessage; /** First message on an admin connection; password must match `RQ_ADMIN_PASSWORD`. */ interface AdminAuthMessage { type: "admin_auth"; password: string; } /** Remove a member from their session and free capacity (same as forced expiry). */ interface AdminKickMemberMessage { type: "admin_kick_member"; connId: string; } /** Drop a still-waiting connection from the queue and close its socket. */ interface AdminKickQueuedMessage { type: "admin_kick_queued"; connId: string; } /** Stop the Reactor session and evict all members. */ interface AdminCloseSessionMessage { type: "admin_close_session"; sessionId: string; } /** Request a fresh snapshot (also sent automatically on room changes). */ interface AdminRefreshMessage { type: "admin_refresh"; } type AdminClientMessage = AdminAuthMessage | AdminKickMemberMessage | AdminKickQueuedMessage | AdminCloseSessionMessage | AdminRefreshMessage; /** Narrowing parse for an inbound server message. Returns null on garbage. */ declare function parseServerMessage(raw: string): ServerMessage | null; /** Narrowing parse for an inbound client message. Returns null on garbage. */ declare function parseClientMessage(raw: string): ClientMessage | null; /** Parse an admin client message. Returns null on garbage or non-admin types. */ declare function parseAdminClientMessage(raw: string): AdminClientMessage | null; /** Parse a server message sent to an admin connection. */ declare function parseAdminServerMessage(raw: string): AdminServerMessage | null; export { ADMIN_MODE_QUERY_KEY, type AdminActionResultMessage, type AdminAuthMessage, type AdminClientMessage, type AdminCloseSessionMessage, type AdminConfigSnapshot, type AdminKickMemberMessage, type AdminKickQueuedMessage, type AdminLogEntry, type AdminLogHistoryMessage, type AdminLogLevel, type AdminLogMessage, type AdminMemberSnapshot, type AdminQueuedUserSnapshot, type AdminReadyMessage, type AdminRefreshMessage, type AdminRejectedMessage, type AdminServerMessage, type AdminSessionSnapshot, type AdminSnapshotMessage, type AdmittedMessage, CLIENT_ID_QUERY_KEY, type ClaimMessage, type ClientMessage, DEFAULTS, DEFAULT_ROOM, type ErrorMessage, type ExpiredMessage, type LeaveMessage, PROTOCOL_VERSION, type QueuePositionMessage, type RejectedMessage, type RequestTokenMessage, type ServerMessage, type SessionEndedMessage, type SessionReadyMessage, TERMINAL_SESSION_STATES, type TimeWarningMessage, type TokenMessage, parseAdminClientMessage, parseAdminServerMessage, parseClientMessage, parseServerMessage };