import type { RoomPolicyV1 } from "../roomPolicy"; import type { CoverSchedulerStatus } from "../handlers/coverScheduler"; import type { TurnCredentials } from "../utils/interfaces"; export interface Channel { label: string; peerIds: string[]; } export interface Peer { peerId: string; peerPublicKey: string; } export interface Message { /** Sender-only random identity for one logical outbound transfer. */ transferId?: string; merkleRootHex: string; sha512Hex: string; fromPeerId: string; filename: string; messageType: number; savedSize: number; totalSize: number; chunksCreated: number; totalChunks: number; channelLabel: string; timestamp: number; chunksReceivedTotal?: number; chunksReceivedReal?: number; retransmits?: number; } export interface IncrementMessageStatsArgs { roomId: string; merkleRootHex?: string; sha512Hex?: string; real?: boolean; retransmit?: boolean; } export interface SetRoomArgs { url: string; id: string; canBeConnectionRelay?: boolean; onlyConnectWithKnownPeers?: boolean; rtcConfig?: RTCConfiguration; /** Public/authenticated room settings only. PIN bytes never enter Redux. */ policy?: RoomPolicyV1; } export interface SetRoomPolicyArgs { roomContext: string; policy: RoomPolicyV1; } export interface SetPeerArgs extends Peer { roomId: string; } export interface SetIceServersArgs { roomId: string; iceServers: RTCIceServer[]; } export interface ApplyRoomResponseArgs { roomUrl: string; roomId: string; roomRequestId: string; requestGeneration: number; turnCredentials: TurnCredentials | null; } export interface ExpireRoomTurnCredentialsArgs { roomUrl: string; expiresAtUnixSeconds: number; requestGeneration: number; } export interface RollbackRoomResponseArgs { roomUrl: string; failedRoomId: string; failedRequestGeneration: number; previousRoomId: string; previousRequestGeneration: number; previousTurnCredentials: TurnCredentials | null; previousSignalingActive: boolean; } export interface SetMessageArgs { roomId: string; transferId?: string; merkleRootHex: string; sha512Hex: string; chunkSize: number; totalSize: number; chunksCreated?: number; totalChunks?: number; timestamp?: number; fromPeerId: string; filename: string; messageType: number; channelLabel: string; } export interface SetMessageAllChunksArgs { roomId: string; transferId?: string; merkleRootHex: string; sha512Hex: string; fromPeerId: string; filename: string; messageType: number; totalSize: number; channelLabel: string; timestamp: number; alsoSendFinishedMessage?: boolean; } export interface SetMessageDeliveredSizeArgs { roomId: string; merkleRootHex: string; sha512Hex: string; fromPeerId: string; deliveredSize: number; } export interface SetChannelArgs { roomId: string; peerId: string; label: string; } export interface DeleteMessageArgs { roomId: string; transferId?: string; merkleRootHex?: string; hashHex?: string; } export interface DeleteChannelArgs { roomId?: string; peerId?: string; label?: string; } export interface SetCanOnlyConnectWithKnownPeers { roomId: string; roomUrl?: string; onlyConnectWithKnownPeers: boolean; } export interface SetPeerCoverStatusArgs { roomId: string; peerId: string; status: CoverSchedulerStatus; } /** * Why the authenticated edge is not (yet) up. "pin-mismatch" also covers a * differing room policy on a PIN room — the confirmation transcript cannot * distinguish them; "policy" is a nopin-room confirmation failure, which can * only be a policy/transcript mismatch. */ export type PeerHandshakeFailureReason = "pin-mismatch" | "pin-throttled" | "policy" | "transport"; export interface PeerHandshakeStatus { status: "authenticating" | "authenticated" | "failed"; reason?: PeerHandshakeFailureReason; /** Absolute epoch ms after which a throttled PIN retry may succeed. */ retryAfter?: number; /** * The stable identity this status describes, in the same hex spelling as * `Peer.peerPublicKey`. A peerId is a per-session signaling handle: a peer * that fails and reconnects arrives under a new one, and a joiner that fails * is dropped from `peers` altogether — so a UI that keys a failure on the * peerId either loses it or leaves a stale one on top of a working room. * The identity is what stays the same across both. * * Optional only because a status persisted by an older build has none. */ peerPublicKey?: string; } /** * `peerPublicKey` is inherited as optional so a caller that only has a peerId * still compiles, but every in-library dispatch supplies it: the edge always * knows the identity it is authenticating against (`epc.withPeerPublicKey`). */ export interface SetPeerHandshakeStatusArgs extends PeerHandshakeStatus { roomId: string; peerId: string; } export interface Room extends SetRoomArgs { policy: RoomPolicyV1; connectingToPeers: boolean; connectedToPeers: boolean; canBeConnectionRelay: boolean; onlyConnectWithKnownAddresses: boolean; /** Caller-owned configuration. Signaling credentials never mutate it. */ callerRtcConfig?: RTCConfiguration; /** Current signaling-owned TURN bundle, replaced atomically per response. */ signalingTurnCredentials?: TurnCredentials | null; /** Compatibility projection derived from the two authoritative inputs. */ rtcConfig: RTCConfiguration; /** Last accepted wire-correlated response generation for this room URL. */ roomResponseGeneration?: number; /** False after deleteRoom so retained room metadata is not auto-rejoined. */ signalingActive?: boolean; peers: Peer[]; /** Eligible signaling membership, independent of currently retained transports. */ knownPeers?: Peer[]; channels: Channel[]; messages: Message[]; /** * protocol-v4: live scheduled-cover status per authenticated peer edge * (peerId → status). Absent means no cover claim may ever be made for that * edge — only an explicit "active" from the runtime is a cover claim, so a * browser gap ("suspended") or teardown ("stopped") is always visible. */ coverStatusByPeer?: Record; /** * Live authenticated-handshake state per peer edge (peerId → status), so a * UI can distinguish "still authenticating" from "PIN did not match" from * "PIN retries throttled until T" instead of showing an eternal * waiting-for-peers state. */ handshakeStatusByPeer?: Record; } /** Fully normalized reducer state; public Room keeps new fields optional. */ export interface ManagedRoom extends Room { callerRtcConfig: RTCConfiguration; signalingTurnCredentials: TurnCredentials | null; roomResponseGeneration: number; signalingActive: boolean; } export declare const defaultRTCConfig: RTCConfiguration; export declare const cloneRTCConfiguration: (configuration: RTCConfiguration) => RTCConfiguration; export declare const rtcConfigurationsEqual: (left: RTCConfiguration, right: RTCConfiguration) => boolean; /** * Replace credentials only for endpoints present in an incoming ICE server. * * This preserves every unrelated caller-supplied STUN/TURN entry and every * non-ICE RTCConfiguration option. Replacing overlapping URLs is required: * TURN REST credentials expire, so retaining the first credential for a URL * would make a later authenticated room join silently keep stale access. */ export declare const mergeIceServers: (existing: readonly RTCIceServer[] | undefined, incoming: readonly RTCIceServer[]) => RTCIceServer[]; /** * Derive the browser configuration from two single-source inputs. * * A managed URL replaces an overlapping caller URL only in this projection. * Clearing the managed bundle therefore restores the caller's exact input, * while rotation cannot accidentally retain an older signaling credential. */ export declare const deriveEffectiveRTCConfig: (callerRtcConfig: RTCConfiguration, signalingTurnCredentials: TurnCredentials | null) => RTCConfiguration; export declare const setRoom: import("@reduxjs/toolkit").ActionCreatorWithPayload, applyRoomResponse: import("@reduxjs/toolkit").ActionCreatorWithPayload, rollbackRoomResponse: import("@reduxjs/toolkit").ActionCreatorWithPayload, expireRoomTurnCredentials: import("@reduxjs/toolkit").ActionCreatorWithPayload, setRoomPolicy: import("@reduxjs/toolkit").ActionCreatorWithPayload, setConnectingToPeers: import("@reduxjs/toolkit").ActionCreatorWithPayload<{ roomId: string; connectingToPeers: boolean; reason?: "redial" | "reconcile"; /** A per-member retry; the listener batches concurrent targets. */ peerId?: string; }, "rooms/setConnectingToPeers">, setConnectedToPeers: import("@reduxjs/toolkit").ActionCreatorWithPayload<{ roomId: string; connectedToPeers: boolean; }, "rooms/setConnectedToPeers">, setConnectionRelay: import("@reduxjs/toolkit").ActionCreatorWithPayload<{ roomId: string; canBeConnectionRelay: boolean; }, "rooms/setConnectionRelay">, setOnlyConnectWithKnownPeers: import("@reduxjs/toolkit").ActionCreatorWithPayload, setPeer: import("@reduxjs/toolkit").ActionCreatorWithPayload, setPeerCoverStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload, setPeerHandshakeStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload, clearPeerHandshakeFailures: import("@reduxjs/toolkit").ActionCreatorWithPayload, allowPeerHandshakeRetry: import("@reduxjs/toolkit").ActionCreatorWithPayload, setChannel: import("@reduxjs/toolkit").ActionCreatorWithPayload, setIceServers: import("@reduxjs/toolkit").ActionCreatorWithPayload, setMessage: import("@reduxjs/toolkit").ActionCreatorWithPayload, setMessageAllChunks: import("@reduxjs/toolkit").ActionCreatorWithPayload, incrementMessageStats: import("@reduxjs/toolkit").ActionCreatorWithPayload, deletePeer: import("@reduxjs/toolkit").ActionCreatorWithPayload<{ peerId: string; roomId?: string; }, "rooms/deletePeer">, deleteChannel: import("@reduxjs/toolkit").ActionCreatorWithPayload, deleteMessage: import("@reduxjs/toolkit").ActionCreatorWithPayload, deleteMessageStateOnly: import("@reduxjs/toolkit").ActionCreatorWithPayload, deleteRoom: import("@reduxjs/toolkit").ActionCreatorWithPayload, purgeAllRooms: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"rooms/purgeAllRooms">, setKnownRoomPeers: import("@reduxjs/toolkit").ActionCreatorWithPayload<{ roomId: string; peers: Peer[]; }, "rooms/setKnownRoomPeers">; export declare const roomSelector: (state: { readonly rooms: TRoom[]; }) => TRoom[]; declare const _default: import("redux").Reducer; export default _default;