import type { EncryptedMessage, HandshakeAccept, HandshakeInit, SessionId } from "./types.js"; import { SbrpErrorCode, SignalCode, SignalReason } from "./types.js"; /** * Frame type discriminant (wire byte). * * Frame types are organized by authority: * - Endpoint frames (0x01-0x03): Forwarded by relay, E2EE * - Signal frame (0x04): Daemon → Relay only * - Keepalive frames (0x10-0x11): Connection-scoped, never forwarded * - Control frame (0x20): Relay → Endpoint only */ export declare const FrameType: { readonly HandshakeInit: 1; readonly HandshakeAccept: 2; readonly Data: 3; readonly Signal: 4; readonly Ping: 16; readonly Pong: 17; readonly Control: 32; }; export type FrameType = (typeof FrameType)[keyof typeof FrameType]; /** * Wire control codes (uint16, §14). * * Codes use ranges for categorization: * - 0x01xx: Authentication (terminal) * - 0x02xx: Routing (terminal) * - 0x03xx: Session (terminal) * - 0x04xx: Wire format (terminal) * - 0x09xx: Throttling (varies: rate_limited=N, backpressure=T) * - 0x10xx: Session state (non-terminal) */ export declare const WireControlCode: { readonly Unauthorized: 257; readonly Forbidden: 258; readonly DaemonNotFound: 513; readonly DaemonOffline: 514; readonly SessionNotFound: 769; readonly SessionExpired: 770; readonly MalformedFrame: 1025; readonly PayloadTooLarge: 1026; readonly InvalidFrameType: 1027; readonly InvalidSessionId: 1028; readonly DisallowedSender: 1029; readonly InternalError: 1537; readonly RateLimited: 2305; readonly Backpressure: 2306; readonly SessionPaused: 4097; readonly SessionResumed: 4098; readonly SessionEnded: 4099; readonly SessionPending: 4100; }; export type WireControlCode = (typeof WireControlCode)[keyof typeof WireControlCode]; /** * Check if a control code is terminal (relay closes WebSocket after sending). * * Fail-safe pattern: only enumerate non-terminal exceptions; unknown/new codes * default to terminal so they never silently keep a session alive. */ export declare function isTerminalCode(code: WireControlCode): boolean; /** Decoded frame header */ export interface FrameHeader { type: FrameType; length: number; sessionId: SessionId; } /** Decoded frame (header + payload) */ export interface Frame extends FrameHeader { payload: Uint8Array; } /** Decoded Control frame payload */ export interface ControlPayload { code: WireControlCode; message: string; } /** Decoded Signal frame payload */ export interface SignalPayload { signal: SignalCode; reason: SignalReason; } /** Convert SbrpErrorCode to wire format (for relay-transmittable codes only) */ export declare function toWireControlCode(code: SbrpErrorCode): WireControlCode; /** Convert wire control code to SbrpErrorCode */ export declare function fromWireControlCode(code: WireControlCode): SbrpErrorCode; /** * Encode a frame to binary wire format. * * @throws {SbrpError} if payload exceeds MAX_PAYLOAD_SIZE or sessionId is invalid */ export declare function encodeFrame(type: FrameType, sessionId: SessionId, payload: Uint8Array): Uint8Array; /** * Read frame header without decoding payload. * Useful for routing decisions before full decode. * * Validates wire format constraints including non-zero sessionId * for session-bound frames (§13.2). * * @throws {SbrpError} if buffer is too short, length exceeds max, or sessionId invalid */ export declare function readFrameHeader(data: Uint8Array): FrameHeader; /** * Decode a complete frame from binary data. * * Rejects trailing bytes to catch framing mistakes. Use `FrameDecoder` * for streaming scenarios with multiple frames per buffer. * * @throws {SbrpError} if frame is malformed or has trailing bytes */ export declare function decodeFrame(data: Uint8Array): Frame; /** * Encode HandshakeInit to wire frame. * * @throws {SbrpError} if initPublicKey is not exactly 32 bytes or sessionId is invalid */ export declare function encodeHandshakeInit(sessionId: SessionId, init: HandshakeInit): Uint8Array; /** * Encode HandshakeAccept to wire frame. * * Wire layout (128 bytes): identityPublicKey(32) + acceptPublicKey(32) + signature(64) * * @throws {SbrpError} if field sizes are wrong or sessionId is invalid */ export declare function encodeHandshakeAccept(sessionId: SessionId, accept: HandshakeAccept): Uint8Array; /** * Encode Data frame (encrypted message). * * @throws {SbrpError} if data is too short (< nonce + authTag) or sessionId is invalid */ export declare function encodeData(sessionId: SessionId, message: EncryptedMessage): Uint8Array; /** * Encode Signal frame (daemon → relay). * * @param sessionId Session being signaled * @param signal Signal code (ready or close) * @param reason Reason code (for close signal) */ export declare function encodeSignal(sessionId: SessionId, signal: SignalCode, reason?: SignalReason): Uint8Array; /** * Encode Ping frame (connection-scoped keepalive). * * @param payload Optional 0-8 byte payload for RTT measurement */ export declare function encodePing(payload?: Uint8Array): Uint8Array; /** * Encode Pong frame (connection-scoped keepalive response). * * @param payload Payload from corresponding Ping (must be copied) */ export declare function encodePong(payload?: Uint8Array): Uint8Array; /** * Encode Control frame (relay → endpoint). * * @param sessionId Session ID (non-zero for session events, 0 for connection errors) * @param code Control code from WireControlCode * @param message Optional diagnostic message (for errors only) */ export declare function encodeControl(sessionId: SessionId, code: WireControlCode, message?: string): Uint8Array; /** * Decode HandshakeInit from frame. * * @throws {SbrpError} if frame type or payload size is invalid */ export declare function decodeHandshakeInit(frame: Frame): HandshakeInit; /** * Decode HandshakeAccept from frame. * * Wire layout (128 bytes): identityPublicKey(32) + acceptPublicKey(32) + signature(64) * * @throws {SbrpError} if frame type or payload size is invalid */ export declare function decodeHandshakeAccept(frame: Frame): HandshakeAccept; /** * Decode Data frame (encrypted message). * * @throws {SbrpError} if frame type or payload is invalid */ export declare function decodeData(frame: Frame): EncryptedMessage; /** * Decode Signal frame (daemon → relay). * * @throws {SbrpError} if frame type, payload size, or signal values are invalid */ export declare function decodeSignal(frame: Frame): SignalPayload; /** * Decode Control frame (relay → endpoint). * * Invalid UTF-8 sequences in message are replaced with U+FFFD. * * @throws {SbrpError} if frame type or payload is invalid */ export declare function decodeControl(frame: Frame): ControlPayload; /** * Streaming frame decoder for incremental parsing. * * Accumulates bytes and yields complete frames. Useful when frames * may be fragmented across WebSocket messages or TCP reads. * * @example * ```typescript * const decoder = new FrameDecoder(); * ws.on("message", (data) => { * for (const frame of decoder.push(data)) { * handleFrame(frame); * } * }); * ``` */ export declare class FrameDecoder { private buffer; /** * Push data and yield any complete frames. */ push(data: Uint8Array): Generator; /** Reset decoder state, discarding any buffered data */ reset(): void; /** Number of bytes currently buffered */ get bufferedBytes(): number; } //# sourceMappingURL=frame.d.ts.map