@strevokit/meeting-sdk 0.0.0-stage → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/internal/env.ts", "../../signaling-protocol/dist/version.js", "../../signaling-protocol/dist/limits.js", "../../signaling-protocol/dist/rejection.js", "../../signaling-protocol/dist/primitives.js", "../../signaling-protocol/dist/catalog.js", "../../signaling-protocol/dist/rolePolicy.js", "../../signaling-protocol/dist/payloads.js", "../../signaling-protocol/dist/parse.js", "../../signaling-protocol/dist/build.js", "../src/public/errors.ts", "../src/internal/emitter.ts", "../src/internal/errorMapping.ts", "../src/internal/localMedia.ts", "../src/internal/log.ts", "../src/internal/mediaDelivery.ts", "../src/internal/reconnect/backoff.ts", "../src/internal/reconnect/recoveryWindow.ts", "../src/internal/reconnect/reasons.ts", "../src/internal/reconnect/reconnectController.ts", "../src/internal/roster.ts", "../src/internal/signalingSession.ts", "../src/internal/token.ts", "../src/internal/engine.ts", "../src/internal/selectedPair.ts", "../src/internal/qualitySample.ts", "../src/internal/peerLink.ts", "../src/internal/remoteMedia.ts", "../src/internal/peerMediaDelivery.ts", "../src/createMeetingEngine.ts"],
4
+ "sourcesContent": ["/** The subset of the browser `WebSocket` the engine uses. */\nexport interface SocketLike {\n readonly protocol: string;\n readonly readyState: number;\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { readonly data: unknown }) => void) | null;\n onclose: ((event: { readonly code: number; readonly reason: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\n\nexport type SocketConstructor = new (url: string, protocols: string[]) => SocketLike;\n\n/** A listed capture device. Only the fields the engine reads are required. */\nexport interface MediaDeviceInfoLike {\n readonly deviceId: string;\n readonly kind: string;\n}\n\n/** The subset of `navigator.mediaDevices` the engine uses. */\nexport interface MediaDevicesLike {\n getUserMedia?(constraints: MediaStreamConstraints): Promise<MediaStream>;\n getDisplayMedia?(options?: DisplayMediaStreamOptions): Promise<MediaStream>;\n enumerateDevices?(): Promise<readonly MediaDeviceInfoLike[]>;\n}\n\nexport type TimerHandle = unknown;\n\n/** The subset of `RTCRtpTransceiver` the engine uses. */\nexport interface TransceiverLike {\n direction: string;\n readonly sender: { readonly track: MediaStreamTrack | null; replaceTrack(track: MediaStreamTrack | null): Promise<void> };\n readonly receiver: { readonly track: MediaStreamTrack };\n}\n\nexport type SessionDescriptionLike = { readonly type: string; readonly sdp?: string };\n\nexport type IceCandidateLike = {\n readonly candidate: string;\n readonly sdpMid?: string | null;\n readonly sdpMLineIndex?: number | null;\n readonly usernameFragment?: string | null;\n};\n\n/** The fields of an `RTCStats` entry the engine reads; every other field is ignored. */\nexport type StatsReportLike = {\n readonly type: string;\n readonly id: string;\n readonly selectedCandidatePairId?: string;\n readonly localCandidateId?: string;\n readonly remoteCandidateId?: string;\n readonly candidateType?: string;\n readonly state?: string;\n readonly nominated?: boolean;\n readonly currentRoundTripTime?: number;\n readonly roundTripTime?: number;\n readonly jitter?: number;\n readonly packetsLost?: number;\n readonly packetsReceived?: number;\n readonly bytesSent?: number;\n readonly bytesReceived?: number;\n readonly framesDropped?: number;\n readonly frameWidth?: number;\n readonly frameHeight?: number;\n readonly mimeType?: string;\n readonly codecId?: string;\n};\n\n/** The subset of `RTCStatsReport` the engine uses. */\nexport interface StatsCollectionLike {\n forEach(callback: (report: StatsReportLike) => void): void;\n}\n\nexport type IceServerLike = { urls: string | string[]; username?: string; credential?: string };\n\n/** The subset of `RTCConfiguration` the engine reads and writes; other fields pass through untouched. */\nexport type PeerConfigurationLike = { iceServers?: IceServerLike[] };\n\n/** The subset of `RTCPeerConnection` the engine uses. */\nexport interface PeerConnectionLike {\n readonly signalingState: string;\n readonly connectionState?: string;\n readonly iceConnectionState?: string;\n readonly localDescription: SessionDescriptionLike | null;\n onicecandidate: ((event: { readonly candidate: IceCandidateLike | null }) => void) | null;\n onconnectionstatechange: ((event: unknown) => void) | null;\n oniceconnectionstatechange: ((event: unknown) => void) | null;\n addTransceiver(kind: \"audio\" | \"video\", init: { direction: \"sendrecv\" }): TransceiverLike;\n getTransceivers(): TransceiverLike[];\n createOffer(options?: { iceRestart?: boolean }): Promise<SessionDescriptionLike>;\n createAnswer(): Promise<SessionDescriptionLike>;\n getStats(): Promise<StatsCollectionLike>;\n getConfiguration(): PeerConfigurationLike;\n setConfiguration(configuration: PeerConfigurationLike): void;\n setLocalDescription(description: SessionDescriptionLike): Promise<void>;\n setRemoteDescription(description: { type: \"offer\" | \"answer\"; sdp: string }): Promise<void>;\n addIceCandidate(candidate: IceCandidateLike): Promise<void>;\n close(): void;\n}\n\nexport type PeerConnectionConstructor = new (configuration: {\n iceServers: { urls: string[]; username?: string; credential?: string }[];\n}) => PeerConnectionLike;\n\nexport type MediaStreamConstructor = new (tracks: MediaStreamTrack[]) => MediaStream;\n\n/** Browser capabilities the engine depends on, resolved once per engine. */\nexport interface EngineEnvironment {\n readonly WebSocket: SocketConstructor | undefined;\n readonly mediaDevices: MediaDevicesLike | undefined;\n readonly RTCPeerConnection?: PeerConnectionConstructor | undefined;\n readonly MediaStream?: MediaStreamConstructor | undefined;\n setTimeout(callback: () => void, ms: number): TimerHandle;\n clearTimeout(handle: TimerHandle): void;\n now(): number;\n /**\n * Subscribes to the browser regaining connectivity or switching networks. Returns the unsubscribe function.\n * Absent where the platform offers no such signal.\n */\n onNetworkChange?(callback: () => void): () => void;\n}\n\ntype EventTargetLike = {\n addEventListener(type: string, listener: () => void): void;\n removeEventListener(type: string, listener: () => void): void;\n};\n\nfunction subscribeNetworkChange(callback: () => void): () => void {\n const scope = globalThis as { addEventListener?: unknown; navigator?: { connection?: unknown } };\n const targets: { target: EventTargetLike; type: string }[] = [];\n if (typeof scope.addEventListener === \"function\") {\n targets.push({ target: globalThis as unknown as EventTargetLike, type: \"online\" });\n }\n const connection = scope.navigator?.connection as Partial<EventTargetLike> | undefined;\n if (connection && typeof connection.addEventListener === \"function\" && typeof connection.removeEventListener === \"function\") {\n targets.push({ target: connection as EventTargetLike, type: \"change\" });\n }\n for (const { target, type } of targets) target.addEventListener(type, callback);\n return () => {\n for (const { target, type } of targets) target.removeEventListener(type, callback);\n };\n}\n\nlet override: EngineEnvironment | undefined;\n\nexport function defaultEnvironment(): EngineEnvironment {\n const scope = globalThis as {\n WebSocket?: unknown;\n RTCPeerConnection?: unknown;\n MediaStream?: unknown;\n navigator?: { mediaDevices?: MediaDevicesLike };\n };\n return {\n WebSocket: typeof scope.WebSocket === \"function\" ? (scope.WebSocket as SocketConstructor) : undefined,\n mediaDevices: scope.navigator?.mediaDevices,\n RTCPeerConnection:\n typeof scope.RTCPeerConnection === \"function\" ? (scope.RTCPeerConnection as PeerConnectionConstructor) : undefined,\n MediaStream: typeof scope.MediaStream === \"function\" ? (scope.MediaStream as MediaStreamConstructor) : undefined,\n setTimeout: (callback, ms) => globalThis.setTimeout(callback, ms),\n clearTimeout: (handle) => globalThis.clearTimeout(handle as ReturnType<typeof globalThis.setTimeout>),\n now: () => Date.now(),\n onNetworkChange: subscribeNetworkChange,\n };\n}\n\nexport function currentEnvironment(): EngineEnvironment {\n return override ?? defaultEnvironment();\n}\n\n/** Replaces the environment for engines created afterwards; `undefined` restores the default. */\nexport function __setTestEnvironment(environment: EngineEnvironment | undefined): void {\n override = environment;\n}\n", "/** Protocol version stamped on every outbound message. */\nexport const PROTOCOL_VERSION = 1;\n/** Protocol versions a receiver accepts. */\nexport const SUPPORTED_PROTOCOL_VERSIONS = Object.freeze([1]);\n/** Strict check: a JSON number that is an integer in SUPPORTED_PROTOCOL_VERSIONS (no coercion). */\nexport function isSupportedVersion(value) {\n return (typeof value === \"number\" &&\n Number.isInteger(value) &&\n SUPPORTED_PROTOCOL_VERSIONS.includes(value));\n}\n", "/** Maximum UTF-8 byte length of one serialized frame. The realtime service's WebSocket max payload must be at least this. */\nexport const MAX_MESSAGE_BYTES = 65536;\n/** Maximum UTF-8 byte length of `payload.sdp` on `webrtc.offer` / `webrtc.answer`. */\nexport const MAX_SDP_BYTES = 49152;\n/** Maximum length (UTF-16 code units) of `payload.candidate.candidate`. */\nexport const MAX_ICE_CANDIDATE_LENGTH = 1024;\n/** Maximum Unicode code points of `payload.displayName` (matches the API's display_name default). */\nexport const MAX_DISPLAY_NAME_CODE_POINTS = 255;\n/** Maximum length of a reason code. */\nexport const MAX_REASON_LENGTH = 64;\nexport const MAX_SDP_MID_LENGTH = 64;\nexport const MAX_USERNAME_FRAGMENT_LENGTH = 256;\nexport const MAX_SDP_MLINE_INDEX = 1023;\n/** Maximum entries in `session.welcome.iceServers`. */\nexport const MAX_ICE_SERVERS = 4;\n/** Maximum `urls` per `iceServers` entry. */\nexport const MAX_ICE_SERVER_URLS = 4;\n/** Maximum length of one `iceServers[].urls` element. */\nexport const MAX_ICE_SERVER_URL_LENGTH = 256;\nexport const MAX_ICE_SERVER_USERNAME_LENGTH = 256;\nexport const MAX_ICE_SERVER_CREDENTIAL_LENGTH = 256;\n/** Maximum length of `webrtc.quality_sample` codec / MIME token. */\nexport const MAX_QUALITY_CODEC_LENGTH = 64;\n/** Maximum length of ICE candidate-type tokens on quality samples. */\nexport const MAX_QUALITY_CANDIDATE_TYPE_LENGTH = 32;\n/** Upper bound for optional frame width/height on quality samples. */\nexport const MAX_QUALITY_FRAME_DIMENSION = 16384;\n", "/** Stable rejection reason codes (part of the public contract). */\nexport const REJECTION_CODES = Object.freeze([\n \"invalid_envelope\",\n \"unsupported_version\",\n \"unknown_type\",\n \"direction_not_allowed\",\n \"invalid_payload\",\n]);\n/** Stable sub-reasons carried in `SignalingRejection.detail`. */\nexport const REJECTION_DETAILS = Object.freeze([\n \"message_too_large\",\n \"not_text\",\n \"invalid_json\",\n \"not_an_object\",\n \"invalid_ulid\",\n \"payload_not_object\",\n \"missing_field\",\n \"wrong_type\",\n \"too_long\",\n \"out_of_range\",\n \"invalid_format\",\n]);\nconst CODE_MESSAGES = {\n invalid_envelope: \"Message envelope is invalid.\",\n unsupported_version: \"Protocol version is missing or not supported.\",\n unknown_type: \"Message type is not part of the protocol.\",\n direction_not_allowed: \"Message type is not allowed in this direction.\",\n invalid_payload: \"Message payload is invalid.\",\n};\nconst DETAIL_MESSAGES = {\n message_too_large: \"Message exceeds the maximum size.\",\n not_text: \"Message frame must be UTF-8 text.\",\n invalid_json: \"Message frame is not valid JSON.\",\n not_an_object: \"Message must be a JSON object.\",\n invalid_ulid: \"Identifier must be a canonical ULID.\",\n payload_not_object: \"Payload must be a JSON object.\",\n missing_field: \"A required field is missing.\",\n wrong_type: \"A field has the wrong type.\",\n too_long: \"A field is too long.\",\n out_of_range: \"A field is out of range.\",\n invalid_format: \"A field has an invalid format.\",\n};\n/** Build a rejection. Messages come only from fixed templates. */\nexport function reject(code, detail, field) {\n const rejection = {\n code,\n message: detail ? DETAIL_MESSAGES[detail] : CODE_MESSAGES[code],\n };\n if (field !== undefined)\n rejection.field = field;\n if (detail !== undefined)\n rejection.detail = detail;\n return rejection;\n}\n/** Thrown only by outbound helpers (createSignalingMessage / encodeSignalingMessage). */\nexport class SignalingProtocolError extends Error {\n rejection;\n constructor(rejection) {\n super(`${rejection.code}: ${rejection.message}`);\n this.name = \"SignalingProtocolError\";\n this.rejection = rejection;\n }\n}\n", "const ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;\nconst REASON_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;\nconst TIMESTAMP_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d{1,9})?Z$/;\n/** Canonical ULID: 26 chars, uppercase Crockford base32 (the API's public_id format). */\nexport function isCanonicalUlid(value) {\n return typeof value === \"string\" && ULID_PATTERN.test(value);\n}\n/** UTF-8 byte length without TextEncoder, so it runs in browsers and Node alike. */\nexport function utf8ByteLength(value) {\n let bytes = 0;\n for (let i = 0; i < value.length; i++) {\n const unit = value.charCodeAt(i);\n if (unit < 0x80) {\n bytes += 1;\n }\n else if (unit < 0x800) {\n bytes += 2;\n }\n else if (unit >= 0xd800 && unit <= 0xdbff && i + 1 < value.length) {\n const next = value.charCodeAt(i + 1);\n if (next >= 0xdc00 && next <= 0xdfff) {\n bytes += 4;\n i++;\n }\n else {\n bytes += 3;\n }\n }\n else {\n bytes += 3;\n }\n }\n return bytes;\n}\n/** Number of Unicode code points (surrogate pairs count once). */\nexport function codePointLength(value) {\n let count = 0;\n for (let i = 0; i < value.length; i++) {\n const unit = value.charCodeAt(i);\n if (unit >= 0xd800 && unit <= 0xdbff && i + 1 < value.length) {\n const next = value.charCodeAt(i + 1);\n if (next >= 0xdc00 && next <= 0xdfff)\n i++;\n }\n count++;\n }\n return count;\n}\nexport function isReasonCode(value) {\n return typeof value === \"string\" && REASON_CODE_PATTERN.test(value);\n}\n/** RFC 3339 UTC timestamp with `Z` suffix and a real calendar date. */\nexport function isRfc3339UtcTimestamp(value) {\n if (typeof value !== \"string\")\n return false;\n const match = TIMESTAMP_PATTERN.exec(value);\n if (!match)\n return false;\n const [year, month, day, hour, minute, second] = match\n .slice(1, 7)\n .map((part) => Number(part));\n if (month < 1 || month > 12 || day < 1)\n return false;\n if (hour > 23 || minute > 59 || second > 59)\n return false;\n const daysInMonth = new Date(Date.UTC(year, month, 0)).getUTCDate();\n return day <= daysInMonth;\n}\n/** Non-null, non-array object whose prototype is Object.prototype or null. */\nexport function isPlainObject(value) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return false;\n }\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n/** Read an own property only; inherited properties are treated as absent. */\nexport function readOwn(obj, key) {\n return Object.hasOwn(obj, key) ? obj[key] : undefined;\n}\nexport function hasOwn(obj, key) {\n return Object.hasOwn(obj, key);\n}\n", "const CATALOG = {\n \"participant.join\": { family: \"participant\", direction: \"client_to_server\" },\n \"participant.joined\": { family: \"participant\", direction: \"server_to_client\" },\n \"participant.leave\": { family: \"participant\", direction: \"client_to_server\" },\n \"participant.left\": { family: \"participant\", direction: \"server_to_client\" },\n \"participant.reconnected\": { family: \"participant\", direction: \"server_to_client\" },\n \"webrtc.offer\": { family: \"webrtc\", direction: \"both\" },\n \"webrtc.answer\": { family: \"webrtc\", direction: \"both\" },\n \"webrtc.ice_candidate\": { family: \"webrtc\", direction: \"both\" },\n \"webrtc.connected\": { family: \"webrtc\", direction: \"client_to_server\" },\n \"webrtc.disconnected\": { family: \"webrtc\", direction: \"client_to_server\" },\n \"webrtc.failed\": { family: \"webrtc\", direction: \"client_to_server\" },\n \"webrtc.quality_sample\": { family: \"webrtc\", direction: \"client_to_server\" },\n \"webrtc.ice_restart\": { family: \"webrtc\", direction: \"both\" },\n \"webrtc.ice_restart_result\": { family: \"webrtc\", direction: \"client_to_server\" },\n \"media.camera.started\": { family: \"media\", direction: \"both\" },\n \"media.camera.stopped\": { family: \"media\", direction: \"both\" },\n \"media.microphone.started\": { family: \"media\", direction: \"both\" },\n \"media.microphone.stopped\": { family: \"media\", direction: \"both\" },\n \"media.screen.started\": { family: \"media\", direction: \"both\" },\n \"media.screen.stopped\": { family: \"media\", direction: \"both\" },\n \"meeting.started\": { family: \"meeting\", direction: \"server_to_client\" },\n \"meeting.ended\": { family: \"meeting\", direction: \"server_to_client\" },\n \"meeting.terminated\": { family: \"meeting\", direction: \"server_to_client\" },\n \"meeting.end\": { family: \"meeting\", direction: \"client_to_server\" },\n \"session.authenticated\": { family: \"session\", direction: \"server_to_client\" },\n \"session.welcome\": { family: \"session\", direction: \"server_to_client\" },\n \"session.rejected\": { family: \"session\", direction: \"server_to_client\" },\n \"session.ice_servers\": { family: \"session\", direction: \"server_to_client\" },\n};\nfor (const entry of Object.values(CATALOG))\n Object.freeze(entry);\n/** Family and allowed direction for every v1 message type. */\nexport const MESSAGE_CATALOG = Object.freeze(CATALOG);\n/** All v1 message types, in catalog order. */\nexport const MESSAGE_TYPES = Object.freeze(Object.keys(CATALOG));\nexport function isMessageType(value) {\n return typeof value === \"string\" && Object.hasOwn(CATALOG, value);\n}\nexport function isDirectionAllowed(type, direction) {\n const allowed = MESSAGE_CATALOG[type].direction;\n return allowed === \"both\" || allowed === direction;\n}\n", "const ALL_ROLES = Object.freeze([\"host\", \"participant\"]);\n/** Types restricted to specific roles; every other type is allowed for all roles. */\nexport const ROLE_POLICY = Object.freeze({\n \"media.screen.started\": Object.freeze([\"host\"]),\n \"media.screen.stopped\": Object.freeze([\"host\"]),\n \"meeting.end\": Object.freeze([\"host\"]),\n});\nexport function allowedRoles(type) {\n return ROLE_POLICY[type] ?? ALL_ROLES;\n}\nexport function isRoleAllowed(type, role) {\n return allowedRoles(type).includes(role);\n}\n", "import { MAX_DISPLAY_NAME_CODE_POINTS, MAX_ICE_CANDIDATE_LENGTH, MAX_ICE_SERVER_CREDENTIAL_LENGTH, MAX_ICE_SERVER_URL_LENGTH, MAX_ICE_SERVER_URLS, MAX_ICE_SERVER_USERNAME_LENGTH, MAX_ICE_SERVERS, MAX_QUALITY_CANDIDATE_TYPE_LENGTH, MAX_QUALITY_CODEC_LENGTH, MAX_QUALITY_FRAME_DIMENSION, MAX_REASON_LENGTH, MAX_SDP_BYTES, MAX_SDP_MID_LENGTH, MAX_SDP_MLINE_INDEX, MAX_USERNAME_FRAGMENT_LENGTH, } from \"./limits.js\";\nimport { codePointLength, hasOwn, isCanonicalUlid, isPlainObject, isReasonCode, isRfc3339UtcTimestamp, utf8ByteLength, } from \"./primitives.js\";\nimport { reject } from \"./rejection.js\";\nclass PayloadFailure {\n rejection;\n constructor(rejection) {\n this.rejection = rejection;\n }\n}\nfunction fail(detail, path) {\n throw new PayloadFailure(reject(\"invalid_payload\", detail, path));\n}\nfunction required(obj, key, path) {\n if (!hasOwn(obj, key))\n fail(\"missing_field\", path);\n return obj[key];\n}\nfunction requiredString(obj, key, path) {\n const value = required(obj, key, path);\n if (typeof value !== \"string\")\n fail(\"wrong_type\", path);\n return value;\n}\nfunction participantRef(obj, key, path, ctx, allowSelf) {\n const value = requiredString(obj, key, path);\n if (!isCanonicalUlid(value))\n fail(\"invalid_ulid\", path);\n if (!allowSelf && value === ctx.participantId)\n fail(\"invalid_format\", path);\n return value;\n}\nfunction reasonCode(obj, key, path) {\n const value = requiredString(obj, key, path);\n if (value.length > MAX_REASON_LENGTH)\n fail(\"too_long\", path);\n if (!isReasonCode(value))\n fail(\"invalid_format\", path);\n return value;\n}\nfunction timestamp(obj, key, path) {\n const value = requiredString(obj, key, path);\n if (!isRfc3339UtcTimestamp(value))\n fail(\"invalid_format\", path);\n return value;\n}\nfunction sdp(obj) {\n const path = \"payload.sdp\";\n const value = requiredString(obj, \"sdp\", path);\n if (value.length === 0)\n fail(\"invalid_format\", path);\n if (value.length > MAX_SDP_BYTES)\n fail(\"too_long\", path);\n if (value.length * 3 > MAX_SDP_BYTES && utf8ByteLength(value) > MAX_SDP_BYTES) {\n fail(\"too_long\", path);\n }\n return value;\n}\nfunction nullableString(obj, key, path, maxLength) {\n const value = obj[key];\n if (value === null)\n return null;\n if (typeof value !== \"string\")\n fail(\"wrong_type\", path);\n if (value.length > maxLength)\n fail(\"too_long\", path);\n return value;\n}\nfunction iceCandidate(obj) {\n const base = \"payload.candidate\";\n const raw = required(obj, \"candidate\", base);\n if (!isPlainObject(raw))\n fail(\"wrong_type\", base);\n const candidate = requiredString(raw, \"candidate\", `${base}.candidate`);\n if (candidate.length > MAX_ICE_CANDIDATE_LENGTH)\n fail(\"too_long\", `${base}.candidate`);\n required(raw, \"sdpMid\", `${base}.sdpMid`);\n const sdpMid = nullableString(raw, \"sdpMid\", `${base}.sdpMid`, MAX_SDP_MID_LENGTH);\n const indexPath = `${base}.sdpMLineIndex`;\n const index = required(raw, \"sdpMLineIndex\", indexPath);\n let sdpMLineIndex = null;\n if (index !== null) {\n if (typeof index !== \"number\")\n fail(\"wrong_type\", indexPath);\n if (!Number.isInteger(index) || index < 0 || index > MAX_SDP_MLINE_INDEX) {\n fail(\"out_of_range\", indexPath);\n }\n sdpMLineIndex = index;\n }\n if (candidate.length > 0 && sdpMid === null && sdpMLineIndex === null) {\n fail(\"invalid_format\", base);\n }\n const value = { candidate, sdpMid, sdpMLineIndex };\n if (hasOwn(raw, \"usernameFragment\")) {\n value.usernameFragment = nullableString(raw, \"usernameFragment\", `${base}.usernameFragment`, MAX_USERNAME_FRAGMENT_LENGTH);\n }\n return value;\n}\nconst empty = () => ({});\nconst sessionDescription = (p, ctx) => ({\n targetParticipantId: participantRef(p, \"targetParticipantId\", \"payload.targetParticipantId\", ctx, false),\n sdp: sdp(p),\n});\nconst peer = (p, ctx) => ({\n peerParticipantId: participantRef(p, \"peerParticipantId\", \"payload.peerParticipantId\", ctx, false),\n});\nfunction role(obj) {\n const value = requiredString(obj, \"role\", \"payload.role\");\n if (value !== \"host\" && value !== \"participant\")\n fail(\"invalid_format\", \"payload.role\");\n return value;\n}\nfunction optionalDisplayName(p, value) {\n if (hasOwn(p, \"displayName\")) {\n const path = \"payload.displayName\";\n const displayName = p.displayName;\n if (typeof displayName !== \"string\")\n fail(\"wrong_type\", path);\n if (displayName.length === 0)\n fail(\"invalid_format\", path);\n if (codePointLength(displayName) > MAX_DISPLAY_NAME_CODE_POINTS)\n fail(\"too_long\", path);\n value.displayName = displayName;\n }\n return value;\n}\nconst ICE_SERVER_URL_SCHEMES = [\"stun:\", \"stuns:\", \"turn:\", \"turns:\"];\nfunction optionalBoundedString(obj, key, path, maxLength, value) {\n if (!hasOwn(obj, key))\n return;\n const raw = obj[key];\n if (typeof raw !== \"string\")\n fail(\"wrong_type\", path);\n if (raw.length > maxLength)\n fail(\"too_long\", path);\n value[key] = raw;\n}\nfunction iceServer(raw, path) {\n if (!isPlainObject(raw))\n fail(\"wrong_type\", path);\n const urlsPath = `${path}.urls`;\n const urls = required(raw, \"urls\", urlsPath);\n if (!Array.isArray(urls))\n fail(\"wrong_type\", urlsPath);\n if (urls.length === 0 || urls.length > MAX_ICE_SERVER_URLS)\n fail(\"out_of_range\", urlsPath);\n const checked = urls.map((url, index) => {\n const urlPath = `${urlsPath}.${index}`;\n if (typeof url !== \"string\")\n fail(\"wrong_type\", urlPath);\n if (url.length > MAX_ICE_SERVER_URL_LENGTH)\n fail(\"too_long\", urlPath);\n if (!ICE_SERVER_URL_SCHEMES.some((scheme) => url.startsWith(scheme) && url.length > scheme.length)) {\n fail(\"invalid_format\", urlPath);\n }\n return url;\n });\n const value = { urls: checked };\n optionalBoundedString(raw, \"username\", `${path}.username`, MAX_ICE_SERVER_USERNAME_LENGTH, value);\n optionalBoundedString(raw, \"credential\", `${path}.credential`, MAX_ICE_SERVER_CREDENTIAL_LENGTH, value);\n return value;\n}\nfunction iceServers(obj) {\n const path = \"payload.iceServers\";\n const servers = required(obj, \"iceServers\", path);\n if (!Array.isArray(servers))\n fail(\"wrong_type\", path);\n if (servers.length > MAX_ICE_SERVERS)\n fail(\"too_long\", path);\n return servers.map((server, index) => iceServer(server, `${path}.${index}`));\n}\nfunction sessionWelcome(p) {\n const value = optionalDisplayName(p, {\n role: role(p),\n joinedAt: timestamp(p, \"joinedAt\", \"payload.joinedAt\"),\n });\n if (hasOwn(p, \"mediaMode\")) {\n const path = \"payload.mediaMode\";\n const mediaMode = p.mediaMode;\n if (typeof mediaMode !== \"string\")\n fail(\"wrong_type\", path);\n if (mediaMode !== \"p2p\" && mediaMode !== \"sfu\")\n fail(\"invalid_format\", path);\n value.mediaMode = mediaMode;\n }\n if (hasOwn(p, \"iceServers\"))\n value.iceServers = iceServers(p);\n if (hasOwn(p, \"iceServersExpireAt\")) {\n value.iceServersExpireAt = timestamp(p, \"iceServersExpireAt\", \"payload.iceServersExpireAt\");\n }\n return value;\n}\nfunction optionalNonNegativeNumber(obj, key, path, value) {\n if (!hasOwn(obj, key))\n return;\n const raw = obj[key];\n if (typeof raw !== \"number\" || !Number.isFinite(raw))\n fail(\"wrong_type\", path);\n if (raw < 0)\n fail(\"out_of_range\", path);\n value[key] = raw;\n}\nfunction optionalUnitInterval(obj, key, path, value) {\n if (!hasOwn(obj, key))\n return;\n const raw = obj[key];\n if (typeof raw !== \"number\" || !Number.isFinite(raw))\n fail(\"wrong_type\", path);\n if (raw < 0 || raw > 1)\n fail(\"out_of_range\", path);\n value[key] = raw;\n}\nfunction optionalNonNegativeInt(obj, key, path, value) {\n if (!hasOwn(obj, key))\n return;\n const raw = obj[key];\n if (typeof raw !== \"number\" || !Number.isFinite(raw))\n fail(\"wrong_type\", path);\n if (!Number.isInteger(raw) || raw < 0)\n fail(\"out_of_range\", path);\n value[key] = raw;\n}\nfunction optionalPositiveInt(obj, key, path, max, value) {\n if (!hasOwn(obj, key))\n return;\n const raw = obj[key];\n if (typeof raw !== \"number\" || !Number.isFinite(raw))\n fail(\"wrong_type\", path);\n if (!Number.isInteger(raw) || raw < 1 || raw > max)\n fail(\"out_of_range\", path);\n value[key] = raw;\n}\nfunction looksLikeSdpOrCandidateDump(value) {\n if (value.includes(\"\\r\\n\") || value.includes(\"\\n\"))\n return true;\n if (/^v=0/i.test(value))\n return true;\n if (/^candidate:/i.test(value))\n return true;\n if (/\\bIN IP[46]\\b/i.test(value))\n return true;\n return false;\n}\nfunction optionalShortToken(obj, key, path, maxLength, value) {\n if (!hasOwn(obj, key))\n return;\n const raw = obj[key];\n if (typeof raw !== \"string\")\n fail(\"wrong_type\", path);\n if (raw.length === 0 || raw.length > maxLength)\n fail(raw.length === 0 ? \"invalid_format\" : \"too_long\", path);\n if (looksLikeSdpOrCandidateDump(raw))\n fail(\"invalid_format\", path);\n if (!/^[a-zA-Z0-9._+/-]+$/.test(raw))\n fail(\"invalid_format\", path);\n value[key] = raw;\n}\nconst qualitySample = (p, ctx) => {\n const value = {\n ...peer(p, ctx),\n connectionState: reasonCode(p, \"connectionState\", \"payload.connectionState\"),\n sampledAt: timestamp(p, \"sampledAt\", \"payload.sampledAt\"),\n };\n optionalNonNegativeNumber(p, \"rttMs\", \"payload.rttMs\", value);\n optionalNonNegativeNumber(p, \"jitterMs\", \"payload.jitterMs\", value);\n optionalUnitInterval(p, \"packetLossRatio\", \"payload.packetLossRatio\", value);\n optionalNonNegativeNumber(p, \"sendBitrateBps\", \"payload.sendBitrateBps\", value);\n optionalNonNegativeNumber(p, \"receiveBitrateBps\", \"payload.receiveBitrateBps\", value);\n optionalShortToken(p, \"iceLocalCandidateType\", \"payload.iceLocalCandidateType\", MAX_QUALITY_CANDIDATE_TYPE_LENGTH, value);\n optionalShortToken(p, \"iceRemoteCandidateType\", \"payload.iceRemoteCandidateType\", MAX_QUALITY_CANDIDATE_TYPE_LENGTH, value);\n for (const key of [\"path\", \"localPath\", \"remotePath\"]) {\n if (hasOwn(p, key))\n value[key] = reasonCode(p, key, `payload.${key}`);\n }\n optionalNonNegativeInt(p, \"framesDropped\", \"payload.framesDropped\", value);\n optionalPositiveInt(p, \"frameWidth\", \"payload.frameWidth\", MAX_QUALITY_FRAME_DIMENSION, value);\n optionalPositiveInt(p, \"frameHeight\", \"payload.frameHeight\", MAX_QUALITY_FRAME_DIMENSION, value);\n optionalShortToken(p, \"codec\", \"payload.codec\", MAX_QUALITY_CODEC_LENGTH, value);\n if (hasOwn(p, \"incomplete\")) {\n if (typeof p.incomplete !== \"boolean\")\n fail(\"wrong_type\", \"payload.incomplete\");\n value.incomplete = p.incomplete;\n }\n return value;\n};\nconst VALIDATORS = {\n \"participant.join\": empty,\n \"participant.joined\": (p) => optionalDisplayName(p, { role: role(p) }),\n \"participant.leave\": empty,\n \"participant.left\": (p) => ({ reason: reasonCode(p, \"reason\", \"payload.reason\") }),\n \"participant.reconnected\": empty,\n \"webrtc.offer\": sessionDescription,\n \"webrtc.answer\": sessionDescription,\n \"webrtc.ice_candidate\": (p, ctx) => ({\n targetParticipantId: participantRef(p, \"targetParticipantId\", \"payload.targetParticipantId\", ctx, false),\n candidate: iceCandidate(p),\n }),\n \"webrtc.connected\": (p, ctx) => {\n const value = peer(p, ctx);\n for (const key of [\"path\", \"localPath\", \"remotePath\"]) {\n if (hasOwn(p, key))\n value[key] = reasonCode(p, key, `payload.${key}`);\n }\n return value;\n },\n \"webrtc.disconnected\": peer,\n \"webrtc.failed\": (p, ctx) => ({\n ...peer(p, ctx),\n reason: reasonCode(p, \"reason\", \"payload.reason\"),\n }),\n \"webrtc.quality_sample\": qualitySample,\n \"webrtc.ice_restart\": (p, ctx) => ({\n targetParticipantId: participantRef(p, \"targetParticipantId\", \"payload.targetParticipantId\", ctx, false),\n }),\n \"webrtc.ice_restart_result\": (p, ctx) => ({\n ...peer(p, ctx),\n outcome: reasonCode(p, \"outcome\", \"payload.outcome\"),\n }),\n \"media.camera.started\": empty,\n \"media.camera.stopped\": empty,\n \"media.microphone.started\": empty,\n \"media.microphone.stopped\": empty,\n \"media.screen.started\": empty,\n \"media.screen.stopped\": empty,\n \"meeting.started\": (p) => ({ startedAt: timestamp(p, \"startedAt\", \"payload.startedAt\") }),\n \"meeting.ended\": (p, ctx) => {\n const value = { endedAt: timestamp(p, \"endedAt\", \"payload.endedAt\") };\n if (hasOwn(p, \"endedByParticipantId\")) {\n value.endedByParticipantId = participantRef(p, \"endedByParticipantId\", \"payload.endedByParticipantId\", ctx, true);\n }\n return value;\n },\n \"meeting.terminated\": (p) => ({\n terminatedAt: timestamp(p, \"terminatedAt\", \"payload.terminatedAt\"),\n reason: reasonCode(p, \"reason\", \"payload.reason\"),\n }),\n \"meeting.end\": empty,\n \"session.authenticated\": (p) => ({ role: role(p) }),\n \"session.welcome\": sessionWelcome,\n \"session.rejected\": (p) => {\n const value = { code: reasonCode(p, \"code\", \"payload.code\") };\n if (hasOwn(p, \"detail\"))\n value.detail = reasonCode(p, \"detail\", \"payload.detail\");\n return value;\n },\n \"session.ice_servers\": (p) => ({\n iceServers: iceServers(p),\n expiresAt: timestamp(p, \"expiresAt\", \"payload.expiresAt\"),\n }),\n};\n/**\n * Validate a payload for `type`. Builds a fresh object with documented fields only,\n * so unknown fields and hostile keys are never copied.\n */\nexport function validatePayload(type, payload, ctx) {\n try {\n return { ok: true, value: VALIDATORS[type](payload, ctx) };\n }\n catch (error) {\n if (error instanceof PayloadFailure)\n return { ok: false, rejection: error.rejection };\n throw error;\n }\n}\n", "import { isDirectionAllowed, isMessageType } from \"./catalog.js\";\nimport { MAX_MESSAGE_BYTES } from \"./limits.js\";\nimport { validatePayload } from \"./payloads.js\";\nimport { isCanonicalUlid, isPlainObject, readOwn, utf8ByteLength } from \"./primitives.js\";\nimport { reject } from \"./rejection.js\";\nimport { PROTOCOL_VERSION, isSupportedVersion } from \"./version.js\";\nfunction failure(error) {\n return { ok: false, error };\n}\nfunction resolveDirection(options) {\n const direction = options?.direction;\n return direction === \"client_to_server\" || direction === \"server_to_client\"\n ? direction\n : undefined;\n}\n/** Ordered checks (contract steps 3-8): object, version, type, direction, envelope fields, payload. */\nfunction parseValue(input, direction) {\n if (!isPlainObject(input))\n return failure(reject(\"invalid_envelope\", \"not_an_object\"));\n if (!isSupportedVersion(readOwn(input, \"version\"))) {\n return failure(reject(\"unsupported_version\", undefined, \"version\"));\n }\n const type = readOwn(input, \"type\");\n if (!isMessageType(type))\n return failure(reject(\"unknown_type\", undefined, \"type\"));\n if (direction !== undefined && !isDirectionAllowed(type, direction)) {\n return failure(reject(\"direction_not_allowed\", undefined, \"type\"));\n }\n const meetingId = readOwn(input, \"meetingId\");\n if (!isCanonicalUlid(meetingId)) {\n return failure(reject(\"invalid_envelope\", \"invalid_ulid\", \"meetingId\"));\n }\n const participantId = readOwn(input, \"participantId\");\n if (!isCanonicalUlid(participantId)) {\n return failure(reject(\"invalid_envelope\", \"invalid_ulid\", \"participantId\"));\n }\n const payload = readOwn(input, \"payload\");\n if (!isPlainObject(payload)) {\n return failure(reject(\"invalid_envelope\", \"payload_not_object\", \"payload\"));\n }\n const result = validatePayload(type, payload, { participantId });\n if (!result.ok)\n return failure(result.rejection);\n const message = {\n version: PROTOCOL_VERSION,\n type,\n meetingId,\n participantId,\n payload: result.value,\n };\n return { ok: true, message };\n}\n/** Validate an already-parsed value. Never throws. */\nexport function parseSignalingMessage(input, options) {\n try {\n return parseValue(input, resolveDirection(options));\n }\n catch {\n return failure(reject(\"invalid_envelope\"));\n }\n}\nfunction exceedsMaxMessageBytes(frame) {\n if (frame.length > MAX_MESSAGE_BYTES)\n return true;\n if (frame.length * 3 <= MAX_MESSAGE_BYTES)\n return false;\n return utf8ByteLength(frame) > MAX_MESSAGE_BYTES;\n}\n/** Validate a raw text frame: size, then JSON, then full validation. Never throws. */\nexport function decodeSignalingFrame(frame, options) {\n try {\n if (typeof frame !== \"string\")\n return failure(reject(\"invalid_envelope\", \"not_text\"));\n if (exceedsMaxMessageBytes(frame)) {\n return failure(reject(\"invalid_envelope\", \"message_too_large\"));\n }\n let value;\n try {\n value = JSON.parse(frame);\n }\n catch {\n return failure(reject(\"invalid_envelope\", \"invalid_json\"));\n }\n return parseValue(value, resolveDirection(options));\n }\n catch {\n return failure(reject(\"invalid_envelope\"));\n }\n}\n", "import { MAX_MESSAGE_BYTES } from \"./limits.js\";\nimport { parseSignalingMessage } from \"./parse.js\";\nimport { utf8ByteLength } from \"./primitives.js\";\nimport { SignalingProtocolError, reject } from \"./rejection.js\";\nimport { PROTOCOL_VERSION } from \"./version.js\";\n/**\n * Build an outbound message stamped with PROTOCOL_VERSION.\n * Throws SignalingProtocolError if it would not pass validation (outbound data is our own code).\n */\nexport function createSignalingMessage(type, fields) {\n const result = parseSignalingMessage({\n version: PROTOCOL_VERSION,\n type,\n meetingId: fields.meetingId,\n participantId: fields.participantId,\n payload: fields.payload,\n });\n if (!result.ok)\n throw new SignalingProtocolError(result.error);\n return result.message;\n}\n/**\n * Serialize a message to a text frame. The message is re-validated (so only documented\n * fields are sent) and the frame must fit in MAX_MESSAGE_BYTES; otherwise this throws.\n */\nexport function encodeSignalingMessage(message) {\n const result = parseSignalingMessage(message);\n if (!result.ok)\n throw new SignalingProtocolError(result.error);\n const frame = JSON.stringify(result.message);\n if (utf8ByteLength(frame) > MAX_MESSAGE_BYTES) {\n throw new SignalingProtocolError(reject(\"invalid_envelope\", \"message_too_large\"));\n }\n return frame;\n}\n", "/**\n * Every code a {@link MeetingError} can carry. Codes are stable strings; new codes may be added in minor\n * versions, so handle unknown codes generically.\n */\nexport type MeetingErrorCode =\n | \"join_token_invalid\"\n | \"join_token_expired\"\n | \"origin_not_allowed\"\n | \"connection_failed\"\n | \"join_timeout\"\n | \"join_cancelled\"\n | \"connection_lost\"\n | \"session_replaced\"\n | \"service_unavailable\"\n | \"rate_limited\"\n | \"meeting_ended\"\n | \"protocol_error\"\n | \"already_joined\"\n | \"engine_closed\"\n | \"not_joined\"\n | \"not_permitted\"\n | \"permission_denied\"\n | \"device_not_found\"\n | \"device_unavailable\"\n | \"screen_share_cancelled\"\n | \"unsupported_environment\"\n | \"media_connection_failed\"\n | \"media_participant_limit\";\n\ntype ErrorInfo = { readonly message: string; readonly retryable: boolean };\n\nexport const ERROR_INFO: Readonly<Record<MeetingErrorCode, ErrorInfo>> = Object.freeze({\n join_token_invalid: { message: \"This meeting link is not valid. Ask for a new one.\", retryable: false },\n join_token_expired: { message: \"This meeting link has expired. Ask for a new one.\", retryable: true },\n origin_not_allowed: { message: \"This site is not allowed to join this meeting.\", retryable: false },\n connection_failed: {\n message: \"Could not connect to the meeting. Check your connection and try again.\",\n retryable: true,\n },\n join_timeout: { message: \"Joining the meeting took too long. Please try again.\", retryable: true },\n join_cancelled: { message: \"Joining was cancelled.\", retryable: false },\n connection_lost: { message: \"The connection to the meeting was lost.\", retryable: true },\n session_replaced: { message: \"You joined this meeting from another tab or device.\", retryable: false },\n service_unavailable: {\n message: \"The meeting service is temporarily unavailable. Please try again shortly.\",\n retryable: true,\n },\n rate_limited: { message: \"Too many attempts. Please wait a moment and try again.\", retryable: true },\n meeting_ended: { message: \"This meeting has ended.\", retryable: false },\n protocol_error: { message: \"Something went wrong in the meeting connection.\", retryable: false },\n already_joined: { message: \"This meeting is already joined or joining.\", retryable: false },\n engine_closed: {\n message: \"This meeting session has ended. Create a new one to join again.\",\n retryable: false,\n },\n not_joined: { message: \"Join the meeting first.\", retryable: false },\n not_permitted: { message: \"You don't have permission to do that in this meeting.\", retryable: false },\n permission_denied: {\n message: \"Camera or microphone access was blocked. Allow access in your browser settings.\",\n retryable: false,\n },\n device_not_found: { message: \"No camera or microphone was found.\", retryable: false },\n device_unavailable: {\n message: \"Your camera or microphone is being used by another application.\",\n retryable: true,\n },\n screen_share_cancelled: { message: \"Screen sharing was cancelled.\", retryable: false },\n unsupported_environment: {\n message:\n \"This browser does not support meetings. Try a current version of Chrome, Edge, Firefox, or Safari.\",\n retryable: false,\n },\n media_connection_failed: {\n message: \"Audio and video couldn't connect to the other participant. Check your network and try rejoining.\",\n retryable: true,\n },\n media_participant_limit: {\n message: \"Audio and video are available with one other participant in this meeting.\",\n retryable: false,\n },\n});\n\n/**\n * The single error type raised by the SDK. `message` is a short English sentence that is safe to show to users;\n * branch on `code` rather than on `message`.\n */\nexport class MeetingError extends Error {\n override readonly name = \"MeetingError\" as const;\n /** Stable machine-readable code. */\n readonly code: MeetingErrorCode;\n /** True when retrying the same action later (with a fresh token for join errors) may succeed. */\n readonly retryable: boolean;\n\n constructor(code: MeetingErrorCode) {\n const info = ERROR_INFO[code];\n super(info.message);\n this.code = code;\n this.retryable = info.retryable;\n }\n}\n", "import type { Log } from \"./log.js\";\n\ntype Listener<T> = (event: T) => void;\n\n/** Synchronous typed emitter. A throwing listener never affects other listeners or the caller. */\nexport class Emitter<Events extends object> {\n readonly #listeners = new Map<keyof Events, Set<Listener<never>>>();\n readonly #log: Log;\n\n constructor(log: Log) {\n this.#log = log;\n }\n\n on<K extends keyof Events>(type: K, listener: Listener<Events[K]>): () => void {\n let set = this.#listeners.get(type);\n if (!set) {\n set = new Set();\n this.#listeners.set(type, set);\n }\n const entry = listener as Listener<never>;\n set.add(entry);\n return () => {\n this.#listeners.get(type)?.delete(entry);\n };\n }\n\n emit<K extends keyof Events>(type: K, event: Events[K]): void {\n const set = this.#listeners.get(type);\n if (!set || set.size === 0) return;\n for (const listener of [...set]) {\n try {\n (listener as Listener<Events[K]>)(event);\n } catch {\n this.#log.warn(\"event listener threw\", { messageType: String(type) });\n }\n }\n }\n\n clear(): void {\n this.#listeners.clear();\n }\n}\n", "import { MeetingError, type MeetingErrorCode } from \"../public/errors.js\";\nimport type { CloseReason, MediaKind, ParticipantLeftReason } from \"../public/types.js\";\n\nexport type ClosePhase = \"before_joined\" | \"after_joined\";\nexport type MappedClose = { reason: CloseReason; error?: MeetingError };\n\nconst BEFORE_JOINED: Readonly<Record<number, MeetingErrorCode>> = {\n 1001: \"service_unavailable\",\n 4001: \"join_token_invalid\",\n 4002: \"join_token_expired\",\n 4003: \"origin_not_allowed\",\n 4004: \"meeting_ended\",\n 4008: \"join_timeout\",\n 4009: \"session_replaced\",\n 4011: \"protocol_error\",\n 4029: \"rate_limited\",\n 4503: \"service_unavailable\",\n};\n\nconst AFTER_JOINED: Readonly<Record<number, readonly [CloseReason, MeetingErrorCode | undefined]>> = {\n 1000: [\"left\", undefined],\n 1001: [\"error\", \"service_unavailable\"],\n 4001: [\"error\", \"join_token_invalid\"],\n 4002: [\"error\", \"join_token_expired\"],\n 4003: [\"error\", \"origin_not_allowed\"],\n 4004: [\"meeting_ended\", undefined],\n 4009: [\"session_replaced\", \"session_replaced\"],\n 4011: [\"error\", \"protocol_error\"],\n 4029: [\"error\", \"rate_limited\"],\n 4503: [\"error\", \"service_unavailable\"],\n};\n\n/** Maps a connection close to the public close reason and error. */\nexport function mapClose(code: number, phase: ClosePhase): MappedClose {\n if (phase === \"before_joined\") {\n return { reason: \"join_failed\", error: new MeetingError(BEFORE_JOINED[code] ?? \"connection_failed\") };\n }\n const [reason, errorCode] = AFTER_JOINED[code] ?? [\"connection_lost\", \"connection_lost\"];\n return errorCode ? { reason, error: new MeetingError(errorCode) } : { reason };\n}\n\nconst REJECTIONS: Readonly<Record<string, MeetingErrorCode>> = {\n rate_limited: \"rate_limited\",\n service_unavailable: \"service_unavailable\",\n role_not_allowed: \"not_permitted\",\n};\n\nexport function mapRejection(code: string): MeetingError {\n return new MeetingError(Object.hasOwn(REJECTIONS, code) ? (REJECTIONS[code] as MeetingErrorCode) : \"protocol_error\");\n}\n\nconst LEFT_REASONS = new Set<ParticipantLeftReason>([\"left\", \"disconnected\", \"removed\", \"meeting_ended\"]);\n\nexport function mapLeftReason(reason: string): ParticipantLeftReason {\n return LEFT_REASONS.has(reason as ParticipantLeftReason) ? (reason as ParticipantLeftReason) : \"other\";\n}\n\nconst DEVICE_ERRORS: Readonly<Record<string, MeetingErrorCode>> = {\n NotAllowedError: \"permission_denied\",\n SecurityError: \"permission_denied\",\n NotFoundError: \"device_not_found\",\n OverconstrainedError: \"device_not_found\",\n NotReadableError: \"device_unavailable\",\n AbortError: \"device_unavailable\",\n TypeError: \"unsupported_environment\",\n};\n\nconst SCREEN_ERRORS: Readonly<Record<string, MeetingErrorCode>> = {\n NotAllowedError: \"screen_share_cancelled\",\n AbortError: \"screen_share_cancelled\",\n SecurityError: \"permission_denied\",\n TypeError: \"unsupported_environment\",\n};\n\nfunction errorName(error: unknown): string | undefined {\n if (typeof error !== \"object\" || error === null) return undefined;\n const name = (error as { name?: unknown }).name;\n return typeof name === \"string\" ? name : undefined;\n}\n\n/** Maps a browser capture failure to a MeetingError. The original error is dropped, never attached. */\nexport function mapMediaError(kind: MediaKind, error: unknown): MeetingError {\n if (error instanceof MeetingError) return error;\n const name = errorName(error);\n const table = kind === \"screen\" ? SCREEN_ERRORS : DEVICE_ERRORS;\n const code = name !== undefined && Object.hasOwn(table, name) ? table[name] : undefined;\n return new MeetingError(code ?? \"device_unavailable\");\n}\n", "import { MeetingError } from \"../public/errors.js\";\nimport type { LocalMedia, MediaKind, MediaState } from \"../public/types.js\";\nimport type { EngineEnvironment } from \"./env.js\";\nimport { mapMediaError } from \"./errorMapping.js\";\nimport type { Log } from \"./log.js\";\n\nexport interface LocalMediaHooks {\n /** Tells the meeting about a local change. Returns false when the connection is gone. */\n announce(kind: MediaKind, enabled: boolean): boolean;\n /** A local change took effect. */\n changed(kind: MediaKind, enabled: boolean, stream: MediaStream | null): void;\n}\n\ntype Operation = { readonly target: boolean; readonly deviceId: string | undefined; readonly promise: Promise<void> };\ntype PerKind<T> = Record<MediaKind, T>;\n\nconst KINDS: readonly MediaKind[] = [\"camera\", \"microphone\", \"screen\"];\n\nfunction stopTracks(stream: MediaStream): void {\n for (const track of stream.getTracks()) {\n try {\n track.stop();\n } catch {\n // Stopping an already-ended track is harmless.\n }\n }\n}\n\nfunction setTracksEnabled(stream: MediaStream, enabled: boolean): void {\n for (const track of stream.getTracks()) track.enabled = enabled;\n}\n\nfunction hasLiveTrack(stream: MediaStream): boolean {\n return stream.getTracks().some((track) => track.readyState === \"live\");\n}\n\n/** The device a capture came from: what the browser reports, else the device that was asked for. */\nfunction deviceOf(stream: MediaStream, requested: string | undefined): string | null {\n const track = stream.getTracks()[0];\n const reported = typeof track?.getSettings === \"function\" ? track.getSettings().deviceId : undefined;\n return reported || requested || null;\n}\n\n/**\n * Local camera, microphone, and screen captures. Changes to one kind run one at a time in call order; a call\n * for the state and device a queued change will reach shares that change's promise.\n */\nexport class LocalMediaController {\n readonly #env: EngineEnvironment;\n readonly #hooks: LocalMediaHooks;\n readonly #log: Log;\n readonly #state: PerKind<boolean> = { camera: false, microphone: false, screen: false };\n readonly #streams: PerKind<MediaStream | null> = { camera: null, microphone: null, screen: null };\n readonly #devices: PerKind<string | null> = { camera: null, microphone: null, screen: null };\n readonly #tails: PerKind<Promise<void>> = {\n camera: Promise.resolve(),\n microphone: Promise.resolve(),\n screen: Promise.resolve(),\n };\n readonly #queued: PerKind<Operation[]> = { camera: [], microphone: [], screen: [] };\n #released = false;\n #generation = 0;\n\n constructor(env: EngineEnvironment, hooks: LocalMediaHooks, log: Log) {\n this.#env = env;\n this.#hooks = hooks;\n this.#log = log;\n }\n\n get state(): MediaState {\n return Object.freeze({ ...this.#state });\n }\n\n get streams(): LocalMedia {\n return Object.freeze({\n camera: this.#state.camera ? this.#streams.camera : null,\n microphone: this.#state.microphone ? this.#streams.microphone : null,\n screen: this.#state.screen ? this.#streams.screen : null,\n });\n }\n\n /**\n * Turns `kind` on or off. `deviceId` applies to turning the camera or microphone on: it captures that device, or\n * switches to it in place when `kind` is already on with another device.\n */\n set(kind: MediaKind, enabled: boolean, deviceId?: string): Promise<void> {\n const wanted = enabled && kind !== \"screen\" ? deviceId : undefined;\n const queue = this.#queued[kind];\n const last = queue[queue.length - 1];\n if (last) {\n if (last.target === enabled && last.deviceId === wanted) return last.promise;\n } else if (this.#state[kind] === enabled && !this.#released && !this.#needsSwitch(kind, wanted)) {\n return Promise.resolve();\n }\n const promise = this.#tails[kind].then(() => this.#run(kind, enabled, wanted));\n const operation: Operation = { target: enabled, deviceId: wanted, promise };\n queue.push(operation);\n const settle = (): void => {\n const index = queue.indexOf(operation);\n if (index >= 0) queue.splice(index, 1);\n };\n this.#tails[kind] = promise.then(settle, settle);\n return promise;\n }\n\n /** Turns `kind` off locally without announcing it, because the meeting refused the change. */\n rollback(kind: MediaKind): void {\n if (!this.#state[kind]) return;\n this.#turnOff(kind, false);\n }\n\n /** Stops every capture. Pending and later changes reject with `not_joined`. Emits nothing. */\n releaseAll(): void {\n this.#released = true;\n this.#generation += 1;\n for (const kind of KINDS) {\n const stream = this.#streams[kind];\n if (stream) stopTracks(stream);\n this.#streams[kind] = null;\n this.#devices[kind] = null;\n this.#state[kind] = false;\n }\n }\n\n /** True when `kind` is on and a specific device other than the one in use was asked for. */\n #needsSwitch(kind: MediaKind, deviceId: string | undefined): boolean {\n return this.#state[kind] && deviceId !== undefined && deviceId !== this.#devices[kind];\n }\n\n async #run(kind: MediaKind, enabled: boolean, deviceId: string | undefined): Promise<void> {\n if (this.#released) throw new MeetingError(\"not_joined\");\n if (enabled && this.#needsSwitch(kind, deviceId)) return this.#switchDevice(kind, deviceId as string);\n if (this.#state[kind] === enabled) return;\n if (enabled) await this.#turnOn(kind, deviceId);\n else this.#turnOff(kind, true);\n }\n\n /**\n * Replaces a running capture with one from another device. The new capture is handed on before the old one stops,\n * so the meeting keeps receiving media; nothing is announced because the on/off state does not change. When the\n * new device cannot be captured, the old capture keeps running.\n */\n async #switchDevice(kind: MediaKind, deviceId: string): Promise<void> {\n const generation = this.#generation;\n const stream = await this.#capture(kind, deviceId);\n if (generation !== this.#generation || this.#released || !this.#state[kind]) {\n stopTracks(stream);\n throw new MeetingError(\"not_joined\");\n }\n const previous = this.#streams[kind];\n this.#streams[kind] = stream;\n this.#devices[kind] = deviceOf(stream, deviceId);\n this.#watchEnded(kind, stream);\n this.#hooks.changed(kind, true, stream);\n if (previous) stopTracks(previous);\n }\n\n async #turnOn(kind: MediaKind, deviceId: string | undefined): Promise<void> {\n if (kind === \"microphone\") {\n const kept = this.#streams.microphone;\n const sameDevice = deviceId === undefined || deviceId === this.#devices.microphone;\n if (kept && hasLiveTrack(kept) && sameDevice) {\n setTracksEnabled(kept, true);\n if (!this.#hooks.announce(kind, true)) {\n setTracksEnabled(kept, false);\n throw new MeetingError(\"not_joined\");\n }\n this.#state.microphone = true;\n this.#hooks.changed(kind, true, kept);\n return;\n }\n if (kept && !hasLiveTrack(kept)) {\n stopTracks(kept);\n this.#streams.microphone = null;\n this.#devices.microphone = null;\n }\n }\n\n const generation = this.#generation;\n const stream = await this.#capture(kind, deviceId);\n if (generation !== this.#generation || this.#released || !this.#hooks.announce(kind, true)) {\n stopTracks(stream);\n throw new MeetingError(\"not_joined\");\n }\n // A kept microphone from another device is replaced only once the chosen one is captured.\n const replaced = this.#streams[kind];\n this.#streams[kind] = stream;\n this.#devices[kind] = kind === \"screen\" ? null : deviceOf(stream, deviceId);\n this.#state[kind] = true;\n this.#watchEnded(kind, stream);\n this.#hooks.changed(kind, true, stream);\n if (replaced) stopTracks(replaced);\n }\n\n #turnOff(kind: MediaKind, announce: boolean): void {\n const stream = this.#streams[kind];\n if (kind === \"microphone\") {\n if (stream) setTracksEnabled(stream, false);\n } else {\n if (stream) stopTracks(stream);\n this.#streams[kind] = null;\n this.#devices[kind] = null;\n }\n this.#state[kind] = false;\n const sent = !announce || this.#hooks.announce(kind, false);\n this.#hooks.changed(kind, false, null);\n if (!sent) throw new MeetingError(\"not_joined\");\n }\n\n async #capture(kind: MediaKind, deviceId?: string): Promise<MediaStream> {\n const devices = this.#env.mediaDevices;\n try {\n if (kind === \"screen\") {\n if (typeof devices?.getDisplayMedia !== \"function\") throw new MeetingError(\"unsupported_environment\");\n return await devices.getDisplayMedia({ video: true, audio: false });\n }\n if (typeof devices?.getUserMedia !== \"function\") throw new MeetingError(\"unsupported_environment\");\n const track: boolean | MediaTrackConstraints = deviceId === undefined ? true : { deviceId: { exact: deviceId } };\n return await devices.getUserMedia(kind === \"camera\" ? { video: track, audio: false } : { audio: track, video: false });\n } catch (error) {\n const mapped = mapMediaError(kind, error);\n this.#log.warn(\"capture failed\", { kind, code: mapped.code });\n throw mapped;\n }\n }\n\n /**\n * A capture the browser or device ended. A live camera or unmuted microphone continues on another listed\n * device of that kind when one can be opened. Otherwise the kind turns off and is announced. Screen share\n * always turns off. A muted microphone is dropped and is not replaced.\n */\n #watchEnded(kind: MediaKind, stream: MediaStream): void {\n const onEnded = (): void => {\n if (this.#released || this.#streams[kind] !== stream) return;\n if (!this.#state[kind]) {\n // A muted microphone that ended is dropped, so the next unmute captures again.\n if (kind === \"microphone\") {\n this.#streams.microphone = null;\n this.#devices.microphone = null;\n }\n return;\n }\n if (kind === \"screen\") {\n this.#log.info(\"screen share ended by the browser\", { kind });\n this.set(kind, false).catch(() => undefined);\n return;\n }\n const generation = this.#generation;\n const scheduled = this.#tails[kind].then(() => this.#continueOnAnotherDevice(kind, stream, generation));\n this.#tails[kind] = scheduled.then(\n () => undefined,\n () => undefined,\n );\n };\n for (const track of stream.getTracks()) track.addEventListener(\"ended\", onEnded, { once: true });\n }\n\n /** The capture that just ended is still the one in use, and the meeting has not been released. */\n #stillCurrent(kind: MediaKind, stream: MediaStream, generation: number): boolean {\n return !this.#released && this.#generation === generation && this.#state[kind] && this.#streams[kind] === stream;\n }\n\n /**\n * Opens the first remaining device of `kind` and installs it the same way as a live switch. When none can be\n * opened, turns `kind` off. A release or a newer capture during the attempt stops the new stream and emits nothing.\n */\n async #continueOnAnotherDevice(kind: \"camera\" | \"microphone\", endedStream: MediaStream, generation: number): Promise<void> {\n if (!this.#stillCurrent(kind, endedStream, generation)) return;\n const endedId = this.#devices[kind] ?? deviceOf(endedStream, undefined);\n const candidates = await this.#otherDevices(kind, endedId);\n if (!this.#stillCurrent(kind, endedStream, generation)) return;\n for (const deviceId of candidates) {\n if (!this.#stillCurrent(kind, endedStream, generation)) return;\n let stream: MediaStream;\n try {\n stream = await this.#capture(kind, deviceId);\n } catch {\n continue;\n }\n if (!this.#stillCurrent(kind, endedStream, generation)) {\n stopTracks(stream);\n return;\n }\n this.#streams[kind] = stream;\n this.#devices[kind] = deviceOf(stream, deviceId);\n this.#watchEnded(kind, stream);\n this.#hooks.changed(kind, true, stream);\n stopTracks(endedStream);\n this.#log.info(\"capture continued on another device\", { kind, reason: \"capture_fallback\" });\n return;\n }\n if (!this.#stillCurrent(kind, endedStream, generation)) return;\n this.#log.info(\"capture ended\", { kind });\n try {\n this.#turnOff(kind, true);\n } catch {\n // The meeting is already gone; the new capture was never installed.\n }\n }\n\n /** Listed devices of `kind`, excluding the one that just ended and any empty id. */\n async #otherDevices(kind: \"camera\" | \"microphone\", endedId: string | null): Promise<string[]> {\n const devices = this.#env.mediaDevices;\n if (typeof devices?.enumerateDevices !== \"function\") return [];\n let listed: readonly { readonly deviceId: string; readonly kind: string }[];\n try {\n listed = await devices.enumerateDevices();\n } catch {\n return [];\n }\n const wanted = kind === \"camera\" ? \"videoinput\" : \"audioinput\";\n const ids: string[] = [];\n for (const device of listed) {\n if (device.kind !== wanted || !device.deviceId || device.deviceId === endedId || ids.includes(device.deviceId)) continue;\n ids.push(device.deviceId);\n }\n return ids;\n }\n}\n", "import type { MeetingLogger } from \"../public/types.js\";\n\ntype Level = \"debug\" | \"info\" | \"warn\" | \"error\";\nexport type LogFields = Readonly<Record<string, string | number | boolean | undefined>>;\n\nconst ALLOWED_KEYS = new Set([\n \"state\",\n \"code\",\n \"reason\",\n \"kind\",\n \"participantId\",\n \"meetingId\",\n \"closeCode\",\n \"messageType\",\n \"durationMs\",\n \"attempt\",\n \"path\",\n \"localPath\",\n \"remotePath\",\n \"trigger\",\n \"elapsedMs\",\n \"requestId\",\n \"correlationId\",\n \"rttMs\",\n \"jitterMs\",\n \"packetLossRatio\",\n \"sendBitrateBps\",\n \"receiveBitrateBps\",\n \"incomplete\",\n]);\n\nconst JWT_SHAPE = /[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]*/;\nconst NEGOTIATION_CONTENT = /sdp|candidate/i;\n\nfunction isSafeString(value: string): boolean {\n return !JWT_SHAPE.test(value) && !NEGOTIATION_CONTENT.test(value);\n}\n\nfunction sanitize(fields: LogFields | undefined): Record<string, string | number | boolean> | undefined {\n if (!fields) return undefined;\n const out: Record<string, string | number | boolean> = {};\n for (const [key, value] of Object.entries(fields)) {\n if (!ALLOWED_KEYS.has(key) || value === undefined) continue;\n if (typeof value === \"string\" && !isSafeString(value)) continue;\n out[key] = value;\n }\n return out;\n}\n\n/** Wraps the consumer's logger: allow-listed fields only, and a throwing logger never affects the engine. */\nexport class Log {\n readonly #logger: MeetingLogger | undefined;\n\n constructor(logger: MeetingLogger | undefined) {\n this.#logger = logger;\n }\n\n debug(message: string, fields?: LogFields): void {\n this.#write(\"debug\", message, fields);\n }\n\n info(message: string, fields?: LogFields): void {\n this.#write(\"info\", message, fields);\n }\n\n warn(message: string, fields?: LogFields): void {\n this.#write(\"warn\", message, fields);\n }\n\n error(message: string, fields?: LogFields): void {\n this.#write(\"error\", message, fields);\n }\n\n #write(level: Level, message: string, fields: LogFields | undefined): void {\n const logger = this.#logger;\n if (!logger) return;\n try {\n const method = logger[level];\n if (typeof method !== \"function\") return;\n const safe = sanitize(fields);\n if (safe) method.call(logger, message, safe);\n else method.call(logger, message);\n } catch {\n // A failing logger must never break the meeting.\n }\n }\n}\n", "import type { IceServerConfig, PayloadMap, SignalingMessage } from \"@strevokit/signaling-protocol\";\nimport type {\n MediaConnectionStatus,\n MediaKind,\n MediaState,\n MeetingEventMap,\n RemoteMedia,\n} from \"../public/types.js\";\nimport type { PeerOutboundType } from \"./signalingSession.js\";\n\nexport type DeliveryEventType = \"remote_media_changed\" | \"media_connection_changed\" | \"error\";\n\nexport interface PresentPeer {\n readonly id: string;\n readonly media: MediaState;\n}\n\n/** What a media delivery strategy may use from the engine. */\nexport interface MediaDeliveryContext {\n /** Self. */\n readonly participantId: string;\n /** From `session.welcome`; may be empty. */\n readonly iceServers: readonly IceServerConfig[];\n /** Epoch ms when the credentials in `iceServers` expire; null when none were given. */\n readonly iceServersExpireAt: number | null;\n /** Sends a media frame through the signaling session. False when it could not be sent. */\n send<T extends PeerOutboundType>(type: T, payload: PayloadMap[T]): boolean;\n /** Emits a public engine event (suppressed after dispose). */\n emit<K extends DeliveryEventType>(type: K, event: MeetingEventMap[K]): void;\n}\n\n/**\n * Carries audio and video between participants. The engine reports roster facts, local capture changes and\n * media-negotiation frames here; strategies never change the public API.\n */\nexport interface MediaDelivery {\n attach(context: MediaDeliveryContext): void;\n /** Every other participant in the committed joined snapshot, reported together. */\n peersPresent(entries: readonly PresentPeer[]): void;\n /** A participant who joined after the joined snapshot was committed. */\n peerPresent(participantId: string, media: MediaState): void;\n peerMediaChanged(participantId: string, kind: MediaKind, enabled: boolean): void;\n peerLeft(participantId: string): void;\n peerReconnected(participantId: string): void;\n onPeerFrame(message: SignalingMessage): void;\n /** Fresh connectivity servers from the platform; `expiresAt` is epoch ms. */\n iceServersUpdated(iceServers: readonly IceServerConfig[], expiresAt: number): void;\n localMediaChanged(kind: MediaKind, stream: MediaStream | null): void;\n /**\n * Whether this delivery can carry screen-share media bits to peers. The engine refuses\n * `startScreenShare` when false, before opening the display-capture prompt.\n */\n supportsScreenShare(): boolean;\n remoteMedia(participantId: string): RemoteMedia;\n connectionStatus(participantId: string): MediaConnectionStatus | null;\n /** Closes everything and emits nothing. Idempotent. */\n close(): void;\n}\n\nexport const NO_REMOTE_MEDIA: RemoteMedia = Object.freeze({ camera: null, microphone: null, screen: null });\n\n/** Announces local media state only; no remote media is delivered. */\nexport class SignalingOnlyMediaDelivery implements MediaDelivery {\n attach(): void {}\n\n peersPresent(): void {}\n\n peerPresent(): void {}\n\n peerMediaChanged(): void {}\n\n peerLeft(): void {}\n\n peerReconnected(): void {}\n\n onPeerFrame(): void {}\n\n iceServersUpdated(): void {}\n\n localMediaChanged(): void {}\n\n supportsScreenShare(): boolean {\n return false;\n }\n\n remoteMedia(): RemoteMedia {\n return NO_REMOTE_MEDIA;\n }\n\n connectionStatus(): MediaConnectionStatus | null {\n return null;\n }\n\n close(): void {}\n}\n", "/** Base delay for the first reconnect open attempt (ms). */\nexport const RECONNECT_BACKOFF_BASE_MS = 500;\n\n/** Exponential growth factor applied per successive attempt. */\nexport const RECONNECT_BACKOFF_FACTOR = 2;\n\n/** Maximum delay between reconnect open attempts (ms). */\nexport const RECONNECT_BACKOFF_CAP_MS = 8000;\n\n/** Fractional jitter applied to each delay (\u00B120%). */\nexport const RECONNECT_BACKOFF_JITTER = 0.2;\n\nexport type BackoffClock = {\n /** Returns a value in [0, 1). Injected for deterministic tests. */\n random(): number;\n};\n\n/**\n * Delay before the given 1-based attempt: `base \u00D7 factor^(n-1)`, capped, with \u00B1jitter.\n * Attempt 1 \u2192 ~500 ms, attempt 2 \u2192 ~1000 ms, \u2026 capped at 8000 ms.\n */\nexport function reconnectBackoffMs(attempt: number, clock: BackoffClock = Math): number {\n const n = Math.max(1, Math.floor(attempt));\n const raw = Math.min(RECONNECT_BACKOFF_CAP_MS, RECONNECT_BACKOFF_BASE_MS * RECONNECT_BACKOFF_FACTOR ** (n - 1));\n const jitter = 1 + (clock.random() * 2 - 1) * RECONNECT_BACKOFF_JITTER;\n return Math.max(0, Math.round(raw * jitter));\n}\n", "/** Default continuous unsuccessful recovery budget (ms). */\nexport const DEFAULT_RECONNECT_WINDOW_MS = 120_000;\n\n/** Lowest allowed `reconnectWindowMs` (tests and production). */\nexport const MIN_RECONNECT_WINDOW_MS = 1_000;\n\n/** Highest allowed `reconnectWindowMs`. */\nexport const MAX_RECONNECT_WINDOW_MS = 600_000;\n\n/**\n * Clamps a consumer-supplied recovery window to the documented safe range.\n * Non-integers and non-finite values fall back to the default.\n */\nexport function clampReconnectWindowMs(value: number | undefined): number {\n if (value === undefined) return DEFAULT_RECONNECT_WINDOW_MS;\n if (typeof value !== \"number\" || !Number.isInteger(value) || !Number.isFinite(value)) {\n return DEFAULT_RECONNECT_WINDOW_MS;\n }\n if (value < MIN_RECONNECT_WINDOW_MS) return MIN_RECONNECT_WINDOW_MS;\n if (value > MAX_RECONNECT_WINDOW_MS) return MAX_RECONNECT_WINDOW_MS;\n return value;\n}\n\nexport type RecoveryWindowState = {\n readonly windowMs: number;\n windowStartedAtMs: number | undefined;\n};\n\n/** Marks the start of the current unsuccessful recovery chain if not already running. */\nexport function markRecoveryWindowStart(state: RecoveryWindowState, nowMs: number): void {\n if (state.windowStartedAtMs === undefined) state.windowStartedAtMs = nowMs;\n}\n\n/** Clears the window so a later failure starts a fresh budget. */\nexport function resetRecoveryWindow(state: RecoveryWindowState): void {\n state.windowStartedAtMs = undefined;\n}\n\n/** True when continuous unsuccessful recovery has exceeded the configured budget. */\nexport function isRecoveryWindowExhausted(state: RecoveryWindowState, nowMs: number): boolean {\n if (state.windowStartedAtMs === undefined) return false;\n return nowMs - state.windowStartedAtMs >= state.windowMs;\n}\n", "/** Stable reason classes for reconnect lifecycle logs. */\nexport type ReconnectReasonClass =\n | \"token_refresh_failed\"\n | \"admission_refused\"\n | \"recovery_window_exhausted\"\n | \"meeting_ended\"\n | \"session_replaced\"\n | \"left\"\n | \"hard_failure\";\n\n/** Why a reconnect chain started. */\nexport type ReconnectTrigger = \"meeting_service\" | \"media\" | \"network\" | \"visibility\";\n\nconst REASON_CLASSES = new Set<ReconnectReasonClass>([\n \"token_refresh_failed\",\n \"admission_refused\",\n \"recovery_window_exhausted\",\n \"meeting_ended\",\n \"session_replaced\",\n \"left\",\n \"hard_failure\",\n]);\n\nconst TRIGGERS = new Set<ReconnectTrigger>([\"meeting_service\", \"media\", \"network\", \"visibility\"]);\n\n/** Keys that must never appear on reconnect logger fields. */\nconst FORBIDDEN_FIELD_KEYS = new Set([\n \"token\",\n \"joinToken\",\n \"join_token\",\n \"authorization\",\n \"Authorization\",\n \"password\",\n \"credential\",\n \"sdp\",\n \"candidate\",\n \"ice\",\n]);\n\nexport type ReconnectLogFields = Readonly<{\n attempt: number;\n trigger?: ReconnectTrigger;\n elapsedMs?: number;\n reason?: ReconnectReasonClass;\n meetingId?: string;\n participantId?: string;\n requestId?: string;\n correlationId?: string;\n}>;\n\nexport function isReconnectReasonClass(value: string): value is ReconnectReasonClass {\n return REASON_CLASSES.has(value as ReconnectReasonClass);\n}\n\nexport function isReconnectTrigger(value: string): value is ReconnectTrigger {\n return TRIGGERS.has(value as ReconnectTrigger);\n}\n\n/**\n * Builds sanitize-ready logger fields for a reconnect lifecycle entry.\n * Drops forbidden token-shaped keys and undefined values.\n */\nexport function reconnectLogFields(input: {\n attempt: number;\n trigger?: ReconnectTrigger;\n elapsedMs?: number;\n reason?: ReconnectReasonClass;\n meetingId?: string;\n participantId?: string;\n requestId?: string;\n correlationId?: string;\n /** Extra keys are ignored when forbidden; only opaque id-like extras are accepted. */\n [key: string]: string | number | boolean | undefined;\n}): Record<string, string | number | boolean> {\n const out: Record<string, string | number | boolean> = {};\n const attempt = Math.max(1, Math.floor(input.attempt));\n out.attempt = attempt;\n if (input.trigger !== undefined && isReconnectTrigger(input.trigger)) out.trigger = input.trigger;\n if (typeof input.elapsedMs === \"number\" && Number.isFinite(input.elapsedMs) && input.elapsedMs >= 0) {\n out.elapsedMs = Math.floor(input.elapsedMs);\n }\n if (input.reason !== undefined && isReconnectReasonClass(input.reason)) out.reason = input.reason;\n for (const key of [\"meetingId\", \"participantId\", \"requestId\", \"correlationId\"] as const) {\n const value = input[key];\n if (typeof value === \"string\" && value.length > 0) out[key] = value;\n }\n for (const key of Object.keys(input)) {\n if (FORBIDDEN_FIELD_KEYS.has(key)) {\n // Intentionally omitted \u2014 never accept token-shaped keys.\n }\n }\n return out;\n}\n\n/** Asserts a proposed field map never carries forbidden keys (for tests and callers). */\nexport function assertNoForbiddenReconnectFields(fields: Readonly<Record<string, unknown>>): void {\n for (const key of Object.keys(fields)) {\n if (FORBIDDEN_FIELD_KEYS.has(key)) {\n throw new Error(`forbidden reconnect log field: ${key}`);\n }\n }\n}\n", "import type { MeetingLogger } from \"../../public/types.js\";\nimport type { EngineEnvironment, TimerHandle } from \"../env.js\";\nimport { reconnectBackoffMs, type BackoffClock } from \"./backoff.js\";\nimport {\n isRecoveryWindowExhausted,\n markRecoveryWindowStart,\n resetRecoveryWindow,\n type RecoveryWindowState,\n} from \"./recoveryWindow.js\";\nimport {\n reconnectLogFields,\n type ReconnectReasonClass,\n type ReconnectTrigger,\n} from \"./reasons.js\";\n\nexport type ReconnectOpenResult = \"recovered\" | \"retry\" | \"failed\" | \"cancelled\";\n\nexport type ReconnectControllerDeps = {\n env: EngineEnvironment;\n logger: MeetingLogger | undefined;\n windowMs: number;\n /** Called to open a new meeting-service connection. Must not throw. */\n open: (attempt: number) => Promise<ReconnectOpenResult>;\n /** Called when the chain ends without recovery (window exhausted or hard failure). */\n onAbandoned?: (reason: ReconnectReasonClass) => void;\n /** Injected for tests; defaults to Math.random. */\n random?: () => number;\n};\n\n/**\n * Schedules bounded meeting-service reconnect opens. Does not talk to signaling itself \u2014\n * the engine supplies `open` and wires success/failure.\n */\nexport class ReconnectController {\n readonly #env: EngineEnvironment;\n readonly #logger: MeetingLogger | undefined;\n readonly #open: (attempt: number) => Promise<ReconnectOpenResult>;\n readonly #onAbandoned: ((reason: ReconnectReasonClass) => void) | undefined;\n readonly #clock: BackoffClock;\n readonly #window: RecoveryWindowState;\n\n #active = false;\n #attempt = 0;\n #trigger: ReconnectTrigger = \"meeting_service\";\n #timer: TimerHandle | undefined;\n #generation = 0;\n #inFlight = false;\n\n constructor(deps: ReconnectControllerDeps) {\n this.#env = deps.env;\n this.#logger = deps.logger;\n this.#open = deps.open;\n this.#onAbandoned = deps.onAbandoned;\n this.#clock = { random: deps.random ?? (() => Math.random()) };\n this.#window = { windowMs: deps.windowMs, windowStartedAtMs: undefined };\n }\n\n get active(): boolean {\n return this.#active;\n }\n\n get attempt(): number {\n return this.#attempt;\n }\n\n /**\n * Starts (or continues) a single active reconnect chain.\n * Overlapping starts while already active are ignored.\n */\n start(trigger: ReconnectTrigger = \"meeting_service\"): void {\n if (this.#active) return;\n this.#active = true;\n this.#trigger = trigger;\n this.#attempt = 0;\n markRecoveryWindowStart(this.#window, this.#env.now());\n this.#scheduleNext(0);\n }\n\n /** Cancels further opens and clears timers. Safe to call when idle. */\n cancel(reason: ReconnectReasonClass = \"left\"): void {\n if (!this.#active && this.#timer === undefined) return;\n const attempt = Math.max(1, this.#attempt);\n const started = this.#window.windowStartedAtMs;\n const elapsedMs = started === undefined ? 0 : Math.max(0, this.#env.now() - started);\n this.#clearTimer();\n this.#generation += 1;\n this.#active = false;\n this.#inFlight = false;\n resetRecoveryWindow(this.#window);\n this.#log(\"info\", \"reconnect cancelled\", {\n attempt,\n elapsedMs,\n reason,\n trigger: this.#trigger,\n });\n }\n\n #scheduleNext(delayMs: number): void {\n this.#clearTimer();\n const generation = this.#generation;\n const run = (): void => {\n if (generation !== this.#generation) return;\n void this.#runAttempt();\n };\n // Immediate first attempt (and zero delays) via microtask so recovery starts without a timer tick.\n if (delayMs <= 0) {\n queueMicrotask(run);\n return;\n }\n this.#timer = this.#env.setTimeout(run, delayMs);\n }\n\n async #runAttempt(): Promise<void> {\n if (!this.#active || this.#inFlight) return;\n const now = this.#env.now();\n if (isRecoveryWindowExhausted(this.#window, now)) {\n this.#abandon(\"recovery_window_exhausted\");\n return;\n }\n this.#attempt += 1;\n const attempt = this.#attempt;\n const generation = this.#generation;\n this.#inFlight = true;\n this.#log(\"info\", \"reconnect attempt started\", { attempt, trigger: this.#trigger });\n let result: ReconnectOpenResult = \"failed\";\n try {\n result = await this.#open(attempt);\n } catch {\n result = \"failed\";\n }\n if (generation !== this.#generation) return;\n this.#inFlight = false;\n const started = this.#window.windowStartedAtMs;\n const elapsedMs = started === undefined ? 0 : Math.max(0, this.#env.now() - started);\n if (result === \"recovered\") {\n this.#active = false;\n resetRecoveryWindow(this.#window);\n this.#log(\"info\", \"reconnect succeeded\", { attempt, elapsedMs, trigger: this.#trigger });\n return;\n }\n if (result === \"cancelled\") {\n this.cancel(\"left\");\n return;\n }\n if (result === \"failed\") {\n this.#abandon(\"hard_failure\");\n return;\n }\n // retry\n if (isRecoveryWindowExhausted(this.#window, this.#env.now())) {\n this.#abandon(\"recovery_window_exhausted\");\n return;\n }\n const delay = reconnectBackoffMs(attempt + 1, this.#clock);\n this.#scheduleNext(delay);\n }\n\n #abandon(reason: ReconnectReasonClass): void {\n const attempt = Math.max(1, this.#attempt);\n const started = this.#window.windowStartedAtMs;\n const elapsedMs = started === undefined ? 0 : Math.max(0, this.#env.now() - started);\n this.#clearTimer();\n this.#generation += 1;\n this.#active = false;\n this.#inFlight = false;\n resetRecoveryWindow(this.#window);\n this.#log(\"warn\", \"reconnect failed\", { attempt, elapsedMs, reason, trigger: this.#trigger });\n this.#onAbandoned?.(reason);\n }\n\n #clearTimer(): void {\n if (this.#timer !== undefined) this.#env.clearTimeout(this.#timer);\n this.#timer = undefined;\n }\n\n #log(\n level: \"info\" | \"warn\",\n message: string,\n fields: {\n attempt: number;\n trigger?: ReconnectTrigger;\n elapsedMs?: number;\n reason?: ReconnectReasonClass;\n },\n ): void {\n const logger = this.#logger;\n if (!logger) return;\n const method = logger[level];\n if (typeof method !== \"function\") return;\n try {\n method.call(logger, message, reconnectLogFields(fields));\n } catch {\n // A failing logger must never break recovery.\n }\n }\n}\n", "import type { MediaKind, MediaState, Participant, ParticipantRole } from \"../public/types.js\";\n\ntype Entry = {\n id: string;\n role: ParticipantRole;\n displayName?: string;\n media: { camera: boolean; microphone: boolean; screen: boolean };\n};\n\nexport type UpsertResult = \"buffered\" | \"added\" | \"updated\";\n\nfunction snapshot(entry: Entry): Participant {\n const media: MediaState = Object.freeze({ ...entry.media });\n const participant: { id: string; role: ParticipantRole; displayName?: string; media: MediaState } = {\n id: entry.id,\n role: entry.role,\n media,\n };\n if (entry.displayName !== undefined) participant.displayName = entry.displayName;\n return Object.freeze(participant);\n}\n\n/**\n * Other participants and their media state. Frames received while joining are buffered until `commit()`, which\n * marks the initial snapshot as complete.\n */\nexport class Roster {\n readonly #entries = new Map<string, Entry>();\n #committed = false;\n #cache: readonly Participant[] | undefined;\n\n get committed(): boolean {\n return this.#committed;\n }\n\n upsertJoined(id: string, role: ParticipantRole, displayName?: string): UpsertResult {\n const existing = this.#entries.get(id);\n this.#cache = undefined;\n if (existing) {\n existing.role = role;\n if (displayName !== undefined) existing.displayName = displayName;\n else delete existing.displayName;\n return this.#committed ? \"updated\" : \"buffered\";\n }\n const entry: Entry = { id, role, media: { camera: false, microphone: false, screen: false } };\n if (displayName !== undefined) entry.displayName = displayName;\n this.#entries.set(id, entry);\n return this.#committed ? \"added\" : \"buffered\";\n }\n\n /** Returns true when the participant's state changed. Unknown participants are ignored after commit. */\n setMedia(id: string, kind: MediaKind, enabled: boolean): boolean {\n let entry = this.#entries.get(id);\n if (!entry) {\n if (this.#committed) return false;\n this.upsertJoined(id, \"participant\");\n entry = this.#entries.get(id);\n if (!entry) return false;\n }\n if (entry.media[kind] === enabled) return false;\n entry.media[kind] = enabled;\n this.#cache = undefined;\n return true;\n }\n\n remove(id: string): boolean {\n const removed = this.#entries.delete(id);\n if (removed) this.#cache = undefined;\n return removed;\n }\n\n commit(): void {\n this.#committed = true;\n this.#cache = undefined;\n }\n\n get(id: string): Participant | undefined {\n const entry = this.#entries.get(id);\n return entry ? snapshot(entry) : undefined;\n }\n\n /** Committed participants in arrival order; empty before commit. */\n list(): readonly Participant[] {\n if (!this.#committed) return Object.freeze([]);\n this.#cache ??= Object.freeze([...this.#entries.values()].map(snapshot));\n return this.#cache;\n }\n\n clear(): void {\n this.#entries.clear();\n this.#cache = undefined;\n }\n\n /** Clears participants and returns to the uncommitted (joining) buffer state. */\n reset(): void {\n this.clear();\n this.#committed = false;\n }\n}\n", "import {\n type MessageType,\n type PayloadMap,\n type SignalingMessage,\n createSignalingMessage,\n decodeSignalingFrame,\n encodeSignalingMessage,\n} from \"@strevokit/signaling-protocol\";\nimport type { EngineEnvironment, SocketLike } from \"./env.js\";\nimport type { Log } from \"./log.js\";\nimport type { Addressing } from \"./token.js\";\n\nexport const SIGNALING_SUBPROTOCOL = \"strevokit.signaling.v1\";\nconst TOKEN_SUBPROTOCOL_PREFIX = \"strevokit.join-token.\";\n/** Close code reported when the connection could not be established or was lost without a close frame. */\nexport const ABNORMAL_CLOSE = 1006;\n\n/** Every frame type the engine sends; all have an empty payload. */\nexport const OUTBOUND_TYPES = Object.freeze([\n \"participant.join\",\n \"participant.leave\",\n \"meeting.end\",\n \"media.camera.started\",\n \"media.camera.stopped\",\n \"media.microphone.started\",\n \"media.microphone.stopped\",\n \"media.screen.started\",\n \"media.screen.stopped\",\n] as const satisfies readonly MessageType[]);\n\nexport type OutboundType = (typeof OUTBOUND_TYPES)[number];\n\n/** Media-negotiation and connection-report frames, sent on behalf of the media delivery. */\nexport const PEER_OUTBOUND_TYPES = Object.freeze([\n \"webrtc.offer\",\n \"webrtc.answer\",\n \"webrtc.ice_candidate\",\n \"webrtc.connected\",\n \"webrtc.disconnected\",\n \"webrtc.failed\",\n \"webrtc.quality_sample\",\n \"webrtc.ice_restart\",\n \"webrtc.ice_restart_result\",\n] as const satisfies readonly MessageType[]);\n\nexport type PeerOutboundType = (typeof PEER_OUTBOUND_TYPES)[number];\n\nexport interface SessionCallbacks {\n onMessage(message: SignalingMessage): void;\n onProtocolError(): void;\n /** `opened` is false when the connection never reached the open state. Called at most once. */\n onClose(code: number, reason: string, opened: boolean): void;\n}\n\nexport interface SessionOptions {\n env: EngineEnvironment;\n realtimeUrl: string;\n token: string;\n addressing: Addressing;\n log: Log;\n callbacks: SessionCallbacks;\n}\n\n/** One signaling connection: handshake, frame encoding and decoding, and close reporting. */\nexport class SignalingSession {\n readonly #env: EngineEnvironment;\n readonly #url: string;\n readonly #addressing: Addressing;\n readonly #log: Log;\n #token: string | undefined;\n #callbacks: SessionCallbacks | undefined;\n #socket: SocketLike | undefined;\n #opened = false;\n\n constructor(options: SessionOptions) {\n this.#env = options.env;\n this.#url = `${options.realtimeUrl}/v1/meetings/${options.addressing.meetingId}/signaling`;\n this.#addressing = options.addressing;\n this.#log = options.log;\n this.#token = options.token;\n this.#callbacks = options.callbacks;\n }\n\n open(): void {\n const token = this.#token;\n this.#token = undefined;\n const Socket = this.#env.WebSocket;\n if (!Socket || token === undefined) {\n this.#fail();\n return;\n }\n let socket: SocketLike;\n try {\n socket = new Socket(this.#url, [SIGNALING_SUBPROTOCOL, `${TOKEN_SUBPROTOCOL_PREFIX}${token}`]);\n } catch {\n this.#log.warn(\"connection could not be created\");\n this.#fail();\n return;\n }\n this.#socket = socket;\n socket.onopen = () => this.#handleOpen();\n socket.onmessage = (event) => this.#handleFrame(event.data);\n socket.onclose = (event) => this.#handleClose(event.code, event.reason);\n socket.onerror = () => undefined;\n }\n\n /** Sends an empty-payload frame. Returns false when the connection is not open or the frame cannot be built. */\n send(type: OutboundType): boolean {\n const socket = this.#socket;\n if (!socket || !this.#opened || !this.#callbacks) return false;\n try {\n const message = createSignalingMessage(type, {\n meetingId: this.#addressing.meetingId,\n participantId: this.#addressing.participantId,\n payload: {},\n });\n socket.send(encodeSignalingMessage(message));\n return true;\n } catch {\n this.#log.warn(\"frame could not be sent\", { messageType: type });\n return false;\n }\n }\n\n /**\n * Sends a media frame. Returns false when the connection is not open or the frame fails validation. The payload\n * is never logged.\n */\n sendPeer<T extends PeerOutboundType>(type: T, payload: PayloadMap[T]): boolean {\n const socket = this.#socket;\n if (!socket || !this.#opened || !this.#callbacks) return false;\n try {\n const message = createSignalingMessage(type, {\n meetingId: this.#addressing.meetingId,\n participantId: this.#addressing.participantId,\n payload,\n });\n socket.send(encodeSignalingMessage(message as SignalingMessage));\n return true;\n } catch {\n this.#log.debug(\"frame could not be sent\", { messageType: type });\n return false;\n }\n }\n\n /** Closes the connection. No further callbacks are made. */\n close(code = 1000): void {\n this.#callbacks = undefined;\n const socket = this.#socket;\n if (!socket) return;\n try {\n socket.close(code);\n } catch {\n // Already closing or closed.\n }\n }\n\n #handleOpen(): void {\n if (!this.#callbacks) return;\n if (this.#socket?.protocol !== SIGNALING_SUBPROTOCOL) {\n this.#log.warn(\"unexpected subprotocol negotiated\");\n const socket = this.#socket;\n this.#fail();\n try {\n socket?.close(1000);\n } catch {\n // Ignore: the connection is being abandoned.\n }\n return;\n }\n this.#opened = true;\n }\n\n #handleFrame(data: unknown): void {\n const callbacks = this.#callbacks;\n if (!callbacks) return;\n const result = decodeSignalingFrame(data, { direction: \"server_to_client\" });\n try {\n if (result.ok) callbacks.onMessage(result.message);\n else {\n this.#log.debug(\"invalid frame received\", { code: result.error.code });\n callbacks.onProtocolError();\n }\n } catch {\n this.#log.error(\"frame handling failed\");\n }\n }\n\n #handleClose(code: number, reason: string): void {\n const callbacks = this.#callbacks;\n this.#callbacks = undefined;\n if (!callbacks) return;\n try {\n callbacks.onClose(typeof code === \"number\" ? code : ABNORMAL_CLOSE, typeof reason === \"string\" ? reason : \"\", this.#opened);\n } catch {\n this.#log.error(\"close handling failed\");\n }\n }\n\n #fail(): void {\n const callbacks = this.#callbacks;\n this.#callbacks = undefined;\n if (!callbacks) return;\n try {\n callbacks.onClose(ABNORMAL_CLOSE, \"\", false);\n } catch {\n this.#log.error(\"close handling failed\");\n }\n }\n}\n", "import { isCanonicalUlid } from \"@strevokit/signaling-protocol\";\nimport { MeetingError } from \"../public/errors.js\";\n\nexport type Addressing = { readonly meetingId: string; readonly participantId: string };\n\n/** Client-side skew margin before treating a token as expired for a new connection (ms). */\nexport const TOKEN_EXPIRY_SKEW_MS = 30_000;\n\nconst BASE64URL = /^[A-Za-z0-9_-]+$/;\n\nfunction decodeSegment(segment: string): unknown {\n if (!BASE64URL.test(segment)) throw new MeetingError(\"join_token_invalid\");\n const padded = segment.replace(/-/g, \"+\").replace(/_/g, \"/\").padEnd(Math.ceil(segment.length / 4) * 4, \"=\");\n const binary = atob(padded);\n const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));\n return JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes));\n}\n\nfunction decodeClaims(token: string): Record<string, unknown> {\n const segments = token.split(\".\");\n if (segments.length !== 3) throw new MeetingError(\"join_token_invalid\");\n let claims: unknown;\n try {\n claims = decodeSegment(segments[1] ?? \"\");\n } catch {\n throw new MeetingError(\"join_token_invalid\");\n }\n if (typeof claims !== \"object\" || claims === null || Array.isArray(claims)) {\n throw new MeetingError(\"join_token_invalid\");\n }\n return claims as Record<string, unknown>;\n}\n\n/**\n * Reads the meeting and participant IDs from a join token so the engine can address the connection. The\n * signature, expiry, and role are deliberately not checked here: the realtime service verifies the token.\n */\nexport function decodeAddressing(token: string): Addressing {\n const claims = decodeClaims(token);\n const { meeting_id: meetingId, participant_id: participantId } = claims;\n if (!isCanonicalUlid(meetingId) || !isCanonicalUlid(participantId)) {\n throw new MeetingError(\"join_token_invalid\");\n }\n return { meetingId, participantId };\n}\n\n/**\n * True when the token still looks usable for opening a new meeting-service connection by client clock.\n * The platform remains the authority; this only decides whether to call `getJoinToken` first.\n */\nexport function isJoinTokenUsable(token: string, nowMs: number, skewMs = TOKEN_EXPIRY_SKEW_MS): boolean {\n try {\n const claims = decodeClaims(token);\n const exp = claims.exp;\n if (typeof exp !== \"number\" || !Number.isFinite(exp)) return false;\n return exp * 1000 > nowMs + skewMs;\n } catch {\n return false;\n }\n}\n", "import {\n type IceServerConfig,\n type MediaMode,\n ROLE_POLICY,\n type SignalingMessage,\n isRoleAllowed,\n} from \"@strevokit/signaling-protocol\";\nimport { MeetingError, type MeetingErrorCode } from \"../public/errors.js\";\nimport type {\n CloseReason,\n ConnectionState,\n LocalMedia,\n MediaConnectionStatus,\n MediaDeviceOptions,\n MediaKind,\n MediaState,\n MeetingEngine,\n MeetingEventListener,\n MeetingEventMap,\n MeetingEventType,\n MeetingLogger,\n Participant,\n ParticipantRole,\n RemoteMedia,\n SelfParticipant,\n} from \"../public/types.js\";\nimport { Emitter } from \"./emitter.js\";\nimport type { EngineEnvironment, TimerHandle } from \"./env.js\";\nimport { mapClose, mapLeftReason, mapRejection } from \"./errorMapping.js\";\nimport { LocalMediaController } from \"./localMedia.js\";\nimport { Log } from \"./log.js\";\nimport { type MediaDelivery, SignalingOnlyMediaDelivery } from \"./mediaDelivery.js\";\nimport {\n ReconnectController,\n type ReconnectOpenResult,\n type ReconnectReasonClass,\n type ReconnectTrigger,\n} from \"./reconnect/index.js\";\nimport { Roster } from \"./roster.js\";\nimport { type OutboundType, SignalingSession } from \"./signalingSession.js\";\nimport { decodeAddressing, isJoinTokenUsable, type Addressing } from \"./token.js\";\n\nconst LEAVE_FALLBACK_MS = 5000;\nconst RESTRICTED_PENDING_MS = 2000;\nconst END_MEETING_TIMEOUT_MS = 10_000;\n\nexport interface EngineConfig {\n token: string;\n realtimeUrl: string;\n joinTimeoutMs: number;\n logger: MeetingLogger | undefined;\n addressing: Addressing;\n env: EngineEnvironment;\n /** Optional callback to mint a fresh join token during automatic recovery. */\n getJoinToken?: () => Promise<string>;\n /** Continuous unsuccessful recovery budget in ms. */\n reconnectWindowMs?: number;\n /** Chooses how media is carried once the welcome says how the platform routes this meeting. */\n createMediaDelivery: MediaDeliveryFactory;\n}\n\nexport type MediaDeliveryFactory = (\n mediaMode: MediaMode | undefined,\n deps: { env: EngineEnvironment; log: Log },\n) => MediaDelivery;\n\nconst MEDIA_KINDS: readonly MediaKind[] = [\"camera\", \"microphone\", \"screen\"];\n\ntype Deferred = { promise: Promise<void>; resolve: () => void; reject: (error: MeetingError) => void };\n\nfunction deferred(): Deferred {\n let resolve!: () => void;\n let reject!: (error: MeetingError) => void;\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n return { promise, resolve, reject };\n}\n\nfunction mediaFrame(type: string): { kind: MediaKind; enabled: boolean } | undefined {\n const match = /^media\\.(camera|microphone|screen)\\.(started|stopped)$/.exec(type);\n if (!match) return undefined;\n return { kind: match[1] as MediaKind, enabled: match[2] === \"started\" };\n}\n\nexport class MeetingEngineImpl implements MeetingEngine {\n readonly #realtimeUrl: string;\n readonly #joinTimeoutMs: number;\n readonly #addressing: Addressing;\n readonly #env: EngineEnvironment;\n readonly #createDelivery: MediaDeliveryFactory;\n readonly #getJoinToken: (() => Promise<string>) | undefined;\n readonly #reconnectWindowMs: number;\n readonly #logger: MeetingLogger | undefined;\n #delivery: MediaDelivery = new SignalingOnlyMediaDelivery();\n readonly #log: Log;\n readonly #emitter: Emitter<MeetingEventMap>;\n readonly #roster = new Roster();\n readonly #media: LocalMediaController;\n\n #token: string | undefined;\n #state: ConnectionState = \"idle\";\n #disposed = false;\n #session: SignalingSession | undefined;\n #self: SelfParticipant | null = null;\n #role: ParticipantRole | undefined;\n #join: Deferred | undefined;\n #leave: Deferred | undefined;\n #joinTimer: TimerHandle | undefined;\n #leaveTimer: TimerHandle | undefined;\n #endMeeting: Deferred | undefined;\n #endMeetingTimer: TimerHandle | undefined;\n #pendingRestricted: { kind: MediaKind; timer: TimerHandle } | undefined;\n #reconnect: ReconnectController | undefined;\n #reconnectOpen: Deferred | undefined;\n #reconnectOutcome: ReconnectOpenResult | undefined;\n #preRecoveryParticipants: readonly Participant[] | undefined;\n #pendingAbandonReason: ReconnectReasonClass | undefined;\n #onVisibility: (() => void) | undefined;\n\n constructor(config: EngineConfig) {\n this.#token = config.token;\n this.#realtimeUrl = config.realtimeUrl;\n this.#joinTimeoutMs = config.joinTimeoutMs;\n this.#addressing = config.addressing;\n this.#env = config.env;\n this.#createDelivery = config.createMediaDelivery;\n this.#getJoinToken = config.getJoinToken;\n this.#reconnectWindowMs = config.reconnectWindowMs ?? 120_000;\n this.#logger = config.logger;\n this.#log = new Log(config.logger);\n this.#emitter = new Emitter(this.#log);\n this.#media = new LocalMediaController(\n config.env,\n {\n announce: (kind, enabled) => this.#announce(kind, enabled),\n changed: (kind, enabled, stream) => {\n this.#emit(\"local_media_changed\", { kind, enabled, stream });\n this.#delivery.localMediaChanged(kind, stream);\n },\n },\n this.#log,\n );\n this.#installVisibilityListener();\n }\n\n get state(): ConnectionState {\n return this.#state;\n }\n\n get self(): SelfParticipant | null {\n return this.#self;\n }\n\n get participants(): readonly Participant[] {\n return this.#roster.list();\n }\n\n get localMediaState(): MediaState {\n return this.#media.state;\n }\n\n get localMedia(): LocalMedia {\n return this.#media.streams;\n }\n\n remoteMedia(participantId: string): RemoteMedia {\n return this.#delivery.remoteMedia(participantId);\n }\n\n mediaConnectionStatus(participantId: string): MediaConnectionStatus | null {\n return this.#delivery.connectionStatus(participantId);\n }\n\n join(): Promise<void> {\n if (this.#state === \"closed\") return Promise.reject(new MeetingError(\"engine_closed\"));\n if (this.#state !== \"idle\") return Promise.reject(new MeetingError(\"already_joined\"));\n const token = this.#token;\n if (!this.#env.WebSocket || token === undefined) return Promise.reject(new MeetingError(\"unsupported_environment\"));\n\n const join = deferred();\n this.#join = join;\n this.#setState(\"connecting\");\n if (!this.#is(\"connecting\")) return join.promise;\n\n this.#joinTimer = this.#env.setTimeout(() => this.#failJoin(new MeetingError(\"join_timeout\")), this.#joinTimeoutMs);\n this.#openSession(token);\n return join.promise;\n }\n\n leave(): Promise<void> {\n switch (this.#state) {\n case \"idle\":\n case \"closed\":\n return Promise.resolve();\n case \"leaving\":\n return this.#leave?.promise ?? Promise.resolve();\n case \"connecting\":\n case \"joining\":\n this.#failJoin(new MeetingError(\"join_cancelled\"));\n return Promise.resolve();\n case \"reconnecting\":\n this.#cancelReconnect(\"left\");\n this.#finish(\"left\");\n return Promise.resolve();\n case \"joined\":\n break;\n }\n const leave = deferred();\n this.#leave = leave;\n this.#media.releaseAll();\n this.#releaseRemoteMedia();\n this.#setState(\"leaving\");\n if (!this.#is(\"leaving\")) return leave.promise;\n if (!this.#session?.send(\"participant.leave\")) {\n this.#finish(\"left\");\n return leave.promise;\n }\n this.#leaveTimer = this.#env.setTimeout(() => this.#finish(\"left\"), LEAVE_FALLBACK_MS);\n return leave.promise;\n }\n\n endMeeting(): Promise<void> {\n if (this.#state !== \"joined\") return Promise.reject(new MeetingError(\"not_joined\"));\n if (this.#role !== \"host\") return Promise.reject(new MeetingError(\"not_permitted\"));\n if (this.#endMeeting) return this.#endMeeting.promise;\n\n const pending = deferred();\n this.#endMeeting = pending;\n if (!this.#session?.send(\"meeting.end\")) {\n this.#endMeeting = undefined;\n return Promise.reject(new MeetingError(\"service_unavailable\"));\n }\n this.#endMeetingTimer = this.#env.setTimeout(() => {\n const p = this.#endMeeting;\n this.#endMeeting = undefined;\n this.#endMeetingTimer = undefined;\n p?.reject(new MeetingError(\"service_unavailable\"));\n }, END_MEETING_TIMEOUT_MS);\n return pending.promise;\n }\n\n enableCamera(options?: MediaDeviceOptions): Promise<void> {\n return this.#mediaCall(\"camera\", true, options?.deviceId);\n }\n\n disableCamera(): Promise<void> {\n return this.#mediaCall(\"camera\", false);\n }\n\n mute(): Promise<void> {\n return this.#mediaCall(\"microphone\", false);\n }\n\n unmute(options?: MediaDeviceOptions): Promise<void> {\n return this.#mediaCall(\"microphone\", true, options?.deviceId);\n }\n\n startScreenShare(): Promise<void> {\n return this.#mediaCall(\"screen\", true);\n }\n\n stopScreenShare(): Promise<void> {\n return this.#mediaCall(\"screen\", false);\n }\n\n on<K extends MeetingEventType>(type: K, listener: MeetingEventListener<K>): () => void {\n if (this.#disposed) return () => undefined;\n return this.#emitter.on(type, listener);\n }\n\n dispose(): void {\n if (this.#disposed) return;\n this.#disposed = true;\n this.#removeVisibilityListener();\n this.#emitter.clear();\n const end = this.#endMeeting;\n this.#endMeeting = undefined;\n this.#cancelReconnect(\"hard_failure\");\n this.#teardown();\n this.#state = \"closed\";\n const join = this.#join;\n this.#join = undefined;\n if (join) {\n join.promise.catch(() => undefined);\n join.reject(new MeetingError(\"join_cancelled\"));\n }\n this.#leave?.resolve();\n end?.reject(new MeetingError(\"engine_closed\"));\n }\n\n #mediaCall(kind: MediaKind, enabled: boolean, deviceId?: string): Promise<void> {\n if (this.#state !== \"joined\") return Promise.reject(new MeetingError(\"not_joined\"));\n if (kind === \"screen\") {\n const type = enabled ? \"media.screen.started\" : \"media.screen.stopped\";\n if (this.#role === undefined || !isRoleAllowed(type, this.#role)) {\n return Promise.reject(new MeetingError(\"not_permitted\"));\n }\n // Refuse before getDisplayMedia when this meeting cannot deliver screen bits to peers.\n if (enabled && !this.#delivery.supportsScreenShare()) {\n return Promise.reject(new MeetingError(\"unsupported_environment\"));\n }\n }\n return this.#media.set(kind, enabled, typeof deviceId === \"string\" && deviceId !== \"\" ? deviceId : undefined);\n }\n\n #announce(kind: MediaKind, enabled: boolean): boolean {\n if (this.#state !== \"joined\") return false;\n const type = `media.${kind}.${enabled ? \"started\" : \"stopped\"}` as OutboundType;\n if (!this.#session?.send(type)) return false;\n if (this.#pendingRestricted?.kind === kind) this.#clearPendingRestricted();\n if (ROLE_POLICY[type]) {\n this.#pendingRestricted = {\n kind,\n timer: this.#env.setTimeout(() => {\n this.#pendingRestricted = undefined;\n }, RESTRICTED_PENDING_MS),\n };\n }\n return true;\n }\n\n #clearPendingRestricted(): void {\n if (this.#pendingRestricted) this.#env.clearTimeout(this.#pendingRestricted.timer);\n this.#pendingRestricted = undefined;\n }\n\n #onMessage(message: SignalingMessage): void {\n const state = this.#state;\n if (state === \"closed\" || state === \"idle\") return;\n const from = message.participantId;\n const isSelf = from === this.#addressing.participantId;\n\n switch (message.type) {\n case \"session.authenticated\": {\n if (state === \"connecting\") {\n this.#role = message.payload.role;\n this.#setState(\"joining\");\n if (!this.#is(\"joining\")) return;\n if (!this.#session?.send(\"participant.join\")) this.#failJoin(new MeetingError(\"connection_failed\"));\n return;\n }\n if (state === \"reconnecting\") {\n this.#role = message.payload.role;\n if (!this.#session?.send(\"participant.join\")) this.#failReconnectOpen(new MeetingError(\"connection_failed\"));\n }\n return;\n }\n case \"session.welcome\": {\n if (state === \"joining\") {\n this.#clearTimer(\"join\");\n this.#roster.commit();\n const payload = message.payload;\n const self: { id: string; role: ParticipantRole; joinedAt: string; displayName?: string } = {\n id: this.#addressing.participantId,\n role: payload.role,\n joinedAt: payload.joinedAt,\n };\n if (payload.displayName !== undefined) self.displayName = payload.displayName;\n this.#self = Object.freeze(self);\n this.#role = payload.role;\n const join = this.#join;\n this.#join = undefined;\n this.#setState(\"joined\");\n this.#emit(\"joined\", { self: this.#self, participants: this.#roster.list() });\n join?.resolve();\n if (this.#is(\"joined\")) {\n const expiresAt = payload.iceServersExpireAt !== undefined ? Date.parse(payload.iceServersExpireAt) : NaN;\n this.#startDelivery(payload.mediaMode, payload.iceServers ?? [], Number.isFinite(expiresAt) ? expiresAt : null);\n }\n return;\n }\n if (state === \"reconnecting\") {\n this.#completeReconnectWelcome(message);\n }\n return;\n }\n case \"session.ice_servers\": {\n if (!this.#is(\"joined\")) {\n this.#log.debug(\"frame ignored\", { messageType: message.type, state });\n return;\n }\n const expiresAt = Date.parse(message.payload.expiresAt);\n if (Number.isFinite(expiresAt)) this.#delivery.iceServersUpdated(message.payload.iceServers, expiresAt);\n return;\n }\n case \"participant.joined\": {\n if (isSelf || state === \"connecting\") return;\n const result = this.#roster.upsertJoined(from, message.payload.role, message.payload.displayName);\n // During recovery, snapshot frames rebuild the roster quietly (no leave/join flash).\n if (state === \"reconnecting\") return;\n const participant = this.#roster.get(from);\n if (result === \"added\" && participant) {\n this.#emit(\"participant_joined\", { participant });\n const present = this.#roster.committed && this.#is(\"joined\") ? this.#roster.get(from) : undefined;\n if (present) this.#delivery.peerPresent(from, present.media);\n }\n return;\n }\n case \"participant.left\": {\n if (isSelf || state === \"connecting\" || state === \"reconnecting\") return;\n if (this.#roster.remove(from) && this.#roster.committed) {\n this.#delivery.peerLeft(from);\n this.#emit(\"participant_left\", { participantId: from, reason: mapLeftReason(message.payload.reason) });\n }\n return;\n }\n case \"participant.reconnected\": {\n if (isSelf || !this.#roster.committed) return;\n // Presence may have removed the peer briefly before the supersede frame arrives \u2014 restore one entry.\n if (!this.#roster.get(from)) {\n this.#roster.upsertJoined(from, \"participant\");\n const restored = this.#roster.get(from);\n // Do not emit participant_joined: reconnect frames lack authoritative role/displayName.\n if (restored && this.#is(\"joined\")) this.#delivery.peerPresent(from, restored.media);\n }\n this.#delivery.peerReconnected(from);\n this.#emit(\"participant_reconnected\", { participantId: from });\n return;\n }\n case \"media.camera.started\":\n case \"media.camera.stopped\":\n case \"media.microphone.started\":\n case \"media.microphone.stopped\":\n case \"media.screen.started\":\n case \"media.screen.stopped\": {\n const change = mediaFrame(message.type);\n if (isSelf || state === \"connecting\" || !change) return;\n if (this.#roster.setMedia(from, change.kind, change.enabled) && this.#roster.committed) {\n this.#emit(\"participant_media_changed\", { participantId: from, kind: change.kind, enabled: change.enabled });\n this.#delivery.peerMediaChanged(from, change.kind, change.enabled);\n }\n return;\n }\n case \"meeting.ended\":\n case \"meeting.terminated\": {\n this.#log.info(\"meeting ended\", { messageType: message.type });\n if (state === \"connecting\" || state === \"joining\") this.#failJoin(new MeetingError(\"meeting_ended\"));\n else {\n this.#cancelReconnect(\"meeting_ended\");\n this.#finish(\"meeting_ended\");\n }\n return;\n }\n case \"session.rejected\": {\n const payload = message.payload as { code: string; detail?: string; ref_type?: string; refType?: string };\n const isEndMeetingRejection =\n payload.ref_type === \"meeting.end\" || payload.refType === \"meeting.end\" || this.#endMeeting !== undefined;\n if (this.#endMeeting && isEndMeetingRejection) {\n const pending = this.#endMeeting;\n this.#clearEndMeetingTimer();\n this.#endMeeting = undefined;\n let mappedCode: MeetingErrorCode = \"service_unavailable\";\n if (payload.code === \"role_not_allowed\") mappedCode = \"not_permitted\";\n else if (payload.code === \"rate_limited\") mappedCode = \"rate_limited\";\n pending.reject(new MeetingError(mappedCode));\n return;\n }\n this.#onRejected(payload.code);\n return;\n }\n case \"webrtc.offer\":\n case \"webrtc.answer\":\n case \"webrtc.ice_candidate\":\n case \"webrtc.ice_restart\":\n this.#delivery.onPeerFrame(message);\n return;\n default:\n this.#log.debug(\"frame ignored\", { messageType: message.type });\n }\n }\n\n #startDelivery(\n mediaMode: MediaMode | undefined,\n iceServers: readonly IceServerConfig[],\n iceServersExpireAt: number | null,\n ): void {\n const delivery = this.#createDelivery(mediaMode, { env: this.#env, log: this.#log });\n this.#delivery = delivery;\n delivery.attach({\n participantId: this.#addressing.participantId,\n iceServers,\n iceServersExpireAt,\n send: (type, payload) => this.#delivery === delivery && (this.#session?.sendPeer(type, payload) ?? false),\n emit: (type, event) => {\n if (this.#delivery === delivery) this.#emit(type, event);\n },\n });\n const local = this.#media.streams;\n for (const kind of MEDIA_KINDS) {\n const stream = local[kind];\n if (stream) delivery.localMediaChanged(kind, stream);\n }\n if (this.#delivery !== delivery || !this.#is(\"joined\")) return;\n delivery.peersPresent(this.#roster.list().map((participant) => ({ id: participant.id, media: participant.media })));\n }\n\n /** Reports every remote media and connection status as gone, then closes the delivery. */\n #releaseRemoteMedia(): void {\n const delivery = this.#delivery;\n for (const participant of this.#roster.list()) {\n const media = delivery.remoteMedia(participant.id);\n for (const kind of MEDIA_KINDS) {\n if (media[kind]) this.#emit(\"remote_media_changed\", { participantId: participant.id, kind, stream: null });\n }\n if (delivery.connectionStatus(participant.id) !== null) {\n this.#emit(\"media_connection_changed\", { participantId: participant.id, status: null });\n }\n }\n delivery.close();\n }\n\n #onRejected(code: string): void {\n const error = mapRejection(code);\n if (this.#state === \"connecting\" || this.#state === \"joining\") {\n this.#failJoin(error);\n return;\n }\n if (this.#state === \"reconnecting\" && this.#reconnectOpen) {\n const session = this.#session;\n this.#session = undefined;\n session?.close();\n this.#failReconnectOpen(error, error.retryable);\n return;\n }\n if (code === \"role_not_allowed\") {\n const pending = this.#pendingRestricted;\n this.#clearPendingRestricted();\n if (pending) this.#media.rollback(pending.kind);\n }\n this.#log.warn(\"request rejected\", { code: error.code });\n this.#emit(\"error\", { error });\n }\n\n #onProtocolError(): void {\n if (this.#state === \"closed\") return;\n this.#emit(\"error\", { error: new MeetingError(\"protocol_error\") });\n }\n\n #onClose(session: SignalingSession, code: number): void {\n if (this.#session !== session) return;\n this.#session = undefined;\n this.#log.info(\"connection closed\", { closeCode: code });\n switch (this.#state) {\n case \"connecting\":\n case \"joining\": {\n const { error } = mapClose(code, \"before_joined\");\n this.#failJoin(error ?? new MeetingError(\"connection_failed\"));\n return;\n }\n case \"joined\": {\n const { reason, error } = mapClose(code, \"after_joined\");\n if (reason === \"left\" || reason === \"meeting_ended\" || reason === \"session_replaced\") {\n this.#finish(reason, error);\n return;\n }\n if (this.#isRecoverableClose(reason, error)) {\n this.#beginRecovery(\"meeting_service\");\n return;\n }\n this.#finish(reason, error);\n return;\n }\n case \"reconnecting\": {\n const { reason, error } = mapClose(code, \"after_joined\");\n if (reason === \"meeting_ended\" || reason === \"session_replaced\") {\n this.#cancelReconnect(reason === \"meeting_ended\" ? \"meeting_ended\" : \"session_replaced\");\n this.#finish(reason, error);\n return;\n }\n if (reason === \"left\") {\n this.#cancelReconnect(\"left\");\n this.#finish(\"left\");\n return;\n }\n // Token failures clear the in-memory token so the next attempt can call getJoinToken.\n const tokenFailure = error?.code === \"join_token_expired\" || error?.code === \"join_token_invalid\";\n if (tokenFailure) this.#token = undefined;\n const retry = tokenFailure || this.#isRecoverableClose(reason, error);\n this.#failReconnectOpen(error ?? new MeetingError(\"connection_lost\"), retry);\n return;\n }\n case \"leaving\":\n this.#finish(\"left\");\n return;\n default:\n return;\n }\n }\n\n #failJoin(error: MeetingError): void {\n if (this.#state !== \"connecting\" && this.#state !== \"joining\") return;\n const join = this.#join;\n this.#join = undefined;\n this.#teardown();\n this.#log.warn(\"join failed\", { code: error.code });\n this.#setState(\"closed\");\n this.#emit(\"closed\", { reason: \"join_failed\", error });\n join?.reject(error);\n }\n\n #finish(reason: CloseReason, error?: MeetingError): void {\n if (this.#state === \"closed\") return;\n const end = this.#endMeeting;\n this.#endMeeting = undefined;\n this.#cancelReconnect(\n reason === \"meeting_ended\"\n ? \"meeting_ended\"\n : reason === \"session_replaced\"\n ? \"session_replaced\"\n : reason === \"left\"\n ? \"left\"\n : \"hard_failure\",\n );\n this.#teardown();\n this.#setState(\"closed\");\n this.#emit(\"closed\", error ? { reason, error } : { reason });\n this.#leave?.resolve();\n if (end) {\n if (reason === \"meeting_ended\") end.resolve();\n else end.reject(error ?? new MeetingError(\"engine_closed\"));\n }\n }\n\n #teardown(): void {\n this.#clearTimer(\"join\");\n this.#clearTimer(\"leave\");\n this.#clearEndMeetingTimer();\n this.#clearPendingRestricted();\n this.#media.releaseAll();\n this.#delivery.close();\n const session = this.#session;\n this.#session = undefined;\n session?.close();\n this.#roster.clear();\n this.#reconnectOpen = undefined;\n this.#reconnectOutcome = undefined;\n this.#preRecoveryParticipants = undefined;\n }\n\n #isRecoverableClose(reason: CloseReason, error: MeetingError | undefined): boolean {\n if (reason === \"connection_lost\") return true;\n if (reason === \"error\" && error?.code === \"service_unavailable\") return true;\n return false;\n }\n\n #beginRecovery(trigger: ReconnectTrigger): void {\n if (this.#state !== \"joined\" && this.#state !== \"reconnecting\") return;\n if (this.#endMeeting) {\n const end = this.#endMeeting;\n this.#endMeeting = undefined;\n this.#clearEndMeetingTimer();\n end.reject(new MeetingError(\"connection_lost\"));\n }\n // Capture once before the first reset so later attempts do not overwrite with an empty roster.\n if (this.#state === \"joined\") this.#preRecoveryParticipants = this.#roster.list();\n this.#pauseDeliveryForRecovery();\n this.#setState(\"reconnecting\");\n if (!this.#is(\"reconnecting\")) return;\n if (!this.#reconnect) {\n this.#reconnect = new ReconnectController({\n env: this.#env,\n logger: this.#logger,\n windowMs: this.#reconnectWindowMs,\n open: () => this.#reconnectOpenAttempt(),\n onAbandoned: (reason) => this.#onReconnectAbandoned(reason),\n });\n }\n this.#reconnect.start(trigger);\n }\n\n #pauseDeliveryForRecovery(): void {\n const delivery = this.#delivery;\n for (const participant of this.#roster.list()) {\n const media = delivery.remoteMedia(participant.id);\n for (const kind of MEDIA_KINDS) {\n if (media[kind]) this.#emit(\"remote_media_changed\", { participantId: participant.id, kind, stream: null });\n }\n if (delivery.connectionStatus(participant.id) !== null) {\n this.#emit(\"media_connection_changed\", { participantId: participant.id, status: null });\n }\n }\n delivery.close();\n this.#delivery = new SignalingOnlyMediaDelivery();\n }\n\n async #reconnectOpenAttempt(): Promise<ReconnectOpenResult> {\n if (this.#state !== \"reconnecting\" || this.#disposed) return \"cancelled\";\n let token: string;\n try {\n token = await this.#resolveReconnectToken();\n } catch (error) {\n const meetingError = error instanceof MeetingError ? error : new MeetingError(\"join_token_invalid\");\n this.#log.warn(\"reconnect token refresh failed\", { code: meetingError.code });\n this.#pendingAbandonReason = \"token_refresh_failed\";\n return \"failed\";\n }\n this.#roster.reset();\n this.#reconnectOutcome = undefined;\n const open = deferred();\n this.#reconnectOpen = open;\n const attemptTimer = this.#env.setTimeout(() => {\n if (this.#reconnectOpen !== open) return;\n const session = this.#session;\n this.#session = undefined;\n session?.close();\n this.#failReconnectOpen(new MeetingError(\"join_timeout\"), true);\n }, this.#joinTimeoutMs);\n this.#openSession(token);\n try {\n await open.promise;\n } catch {\n // Outcome recorded below.\n } finally {\n this.#env.clearTimeout(attemptTimer);\n }\n if (this.#state !== \"reconnecting\" && this.#state !== \"joined\") {\n return this.#reconnectOutcome ?? \"cancelled\";\n }\n return this.#reconnectOutcome ?? \"failed\";\n }\n\n async #resolveReconnectToken(): Promise<string> {\n const current = this.#token;\n if (current !== undefined && isJoinTokenUsable(current, this.#env.now())) return current;\n const refresh = this.#getJoinToken;\n if (!refresh) throw new MeetingError(\"join_token_expired\");\n const next = await refresh();\n if (typeof next !== \"string\" || next.length === 0) throw new MeetingError(\"join_token_invalid\");\n const addressing = decodeAddressing(next);\n if (\n addressing.meetingId !== this.#addressing.meetingId ||\n addressing.participantId !== this.#addressing.participantId\n ) {\n throw new MeetingError(\"join_token_invalid\");\n }\n this.#token = next;\n return next;\n }\n\n #openSession(token: string): void {\n const session = new SignalingSession({\n env: this.#env,\n realtimeUrl: this.#realtimeUrl,\n token,\n addressing: this.#addressing,\n log: this.#log,\n callbacks: {\n onMessage: (message) => this.#onMessage(message),\n onProtocolError: () => this.#onProtocolError(),\n onClose: (code) => this.#onClose(session, code),\n },\n });\n this.#session = session;\n session.open();\n }\n\n #completeReconnectWelcome(message: SignalingMessage & { type: \"session.welcome\" }): void {\n const payload = message.payload;\n if (message.participantId !== this.#addressing.participantId) {\n this.#failReconnectOpen(new MeetingError(\"protocol_error\"));\n return;\n }\n // Snapshot peers arrived as participant.joined frames while reconnecting; commit, join, then emit ID diffs.\n this.#roster.commit();\n const self: { id: string; role: ParticipantRole; joinedAt: string; displayName?: string } = {\n id: this.#addressing.participantId,\n role: payload.role,\n joinedAt: payload.joinedAt,\n };\n if (payload.displayName !== undefined) self.displayName = payload.displayName;\n this.#self = Object.freeze(self);\n this.#role = payload.role;\n this.#reconnectOutcome = \"recovered\";\n const open = this.#reconnectOpen;\n this.#reconnectOpen = undefined;\n this.#setState(\"joined\");\n open?.resolve();\n // Listeners on state_changed / later roster events may leave(); never resume a closed engine.\n if (!this.#is(\"joined\")) {\n this.#preRecoveryParticipants = undefined;\n return;\n }\n this.#emitRecoveryRosterDiff();\n if (!this.#is(\"joined\")) return;\n const expiresAt = payload.iceServersExpireAt !== undefined ? Date.parse(payload.iceServersExpireAt) : NaN;\n this.#startDelivery(payload.mediaMode, payload.iceServers ?? [], Number.isFinite(expiresAt) ? expiresAt : null);\n }\n\n /** Announce peers who joined or left during the outage; leave survivors quiet (no join/leave flash). */\n #emitRecoveryRosterDiff(): void {\n const previous = this.#preRecoveryParticipants ?? [];\n this.#preRecoveryParticipants = undefined;\n const previousIds = new Set(previous.map((participant) => participant.id));\n const next = this.#roster.list();\n const nextIds = new Set(next.map((participant) => participant.id));\n for (const id of previousIds) {\n if (!nextIds.has(id)) this.#emit(\"participant_left\", { participantId: id, reason: \"disconnected\" });\n }\n for (const participant of next) {\n if (!previousIds.has(participant.id)) this.#emit(\"participant_joined\", { participant });\n }\n }\n\n #failReconnectOpen(error: MeetingError, retry = false): void {\n const open = this.#reconnectOpen;\n if (!open) return;\n this.#reconnectOpen = undefined;\n this.#reconnectOutcome = retry ? \"retry\" : \"failed\";\n if (!retry) this.#log.warn(\"reconnect open failed\", { code: error.code });\n open.resolve();\n }\n\n #onReconnectAbandoned(reason: ReconnectReasonClass): void {\n if (this.#state === \"closed\") return;\n const actual = this.#pendingAbandonReason ?? reason;\n this.#pendingAbandonReason = undefined;\n this.#preRecoveryParticipants = undefined;\n this.#reconnect = undefined;\n if (actual === \"meeting_ended\") {\n this.#finish(\"meeting_ended\");\n return;\n }\n if (actual === \"session_replaced\") {\n this.#finish(\"session_replaced\", new MeetingError(\"session_replaced\"));\n return;\n }\n if (actual === \"left\") {\n this.#finish(\"left\");\n return;\n }\n const error =\n actual === \"token_refresh_failed\"\n ? new MeetingError(\"join_token_expired\")\n : new MeetingError(\"connection_lost\");\n this.#finish(\"connection_lost\", error);\n }\n\n #cancelReconnect(reason: ReconnectReasonClass): void {\n const controller = this.#reconnect;\n this.#reconnect = undefined;\n this.#preRecoveryParticipants = undefined;\n if (this.#reconnectOpen) {\n this.#reconnectOutcome = \"cancelled\";\n const open = this.#reconnectOpen;\n this.#reconnectOpen = undefined;\n open.resolve();\n }\n controller?.cancel(reason);\n }\n\n #installVisibilityListener(): void {\n const doc = (globalThis as { document?: { visibilityState?: string; addEventListener?: typeof document.addEventListener } })\n .document;\n if (!doc || typeof doc.addEventListener !== \"function\") return;\n this.#onVisibility = () => {\n if (doc.visibilityState !== \"visible\" || this.#disposed) return;\n if (this.#state === \"reconnecting\") {\n // Continue or restart the recovery chain with a visibility trigger.\n if (this.#reconnect && !this.#reconnect.active) this.#reconnect.start(\"visibility\");\n return;\n }\n if (this.#state === \"joined\" && this.#session === undefined) {\n this.#beginRecovery(\"visibility\");\n }\n };\n doc.addEventListener(\"visibilitychange\", this.#onVisibility);\n }\n\n #removeVisibilityListener(): void {\n const doc = (globalThis as { document?: { removeEventListener?: typeof document.removeEventListener } }).document;\n const handler = this.#onVisibility;\n this.#onVisibility = undefined;\n if (doc && handler && typeof doc.removeEventListener === \"function\") {\n doc.removeEventListener(\"visibilitychange\", handler);\n }\n }\n\n #clearEndMeetingTimer(): void {\n if (this.#endMeetingTimer !== undefined) this.#env.clearTimeout(this.#endMeetingTimer);\n this.#endMeetingTimer = undefined;\n }\n\n #clearTimer(which: \"join\" | \"leave\"): void {\n const handle = which === \"join\" ? this.#joinTimer : this.#leaveTimer;\n if (handle !== undefined) this.#env.clearTimeout(handle);\n if (which === \"join\") this.#joinTimer = undefined;\n else this.#leaveTimer = undefined;\n }\n\n /** Re-reads the state after emitting, since listeners may have changed it. */\n #is(state: ConnectionState): boolean {\n return this.#state === state;\n }\n\n #setState(next: ConnectionState): void {\n const previous = this.#state;\n if (previous === next) return;\n this.#state = next;\n this.#log.info(\"state changed\", { state: next });\n this.#emit(\"state_changed\", { state: next, previous });\n }\n\n #emit<K extends MeetingEventType>(type: K, event: MeetingEventMap[K]): void {\n if (!this.#disposed) this.#emitter.emit(type, event);\n }\n}\n", "import type { PeerConnectionLike, StatsReportLike } from \"./env.js\";\n\nexport type SelectedPair = { readonly localId: string; readonly localType: string; readonly remoteType: string };\n\n/**\n * The candidate-pair report the browser is sending media over: transport.selectedCandidatePairId first, then the\n * succeeded and nominated pair for browsers without that field.\n */\nexport function selectedCandidatePairReport(reports: readonly StatsReportLike[]): StatsReportLike | undefined {\n const byId = new Map(reports.map((report) => [report.id, report]));\n const selectedId = reports.find((r) => r.type === \"transport\" && r.selectedCandidatePairId)?.selectedCandidatePairId;\n return (\n (selectedId ? byId.get(selectedId) : undefined) ??\n reports.find((r) => r.type === \"candidate-pair\" && r.state === \"succeeded\" && r.nominated === true)\n );\n}\n\n/**\n * The candidate pair the browser is sending media over, from its stats: the transport's selected pair first, then\n * the succeeded and nominated pair for browsers without that field. Null when stats are unavailable or incomplete.\n */\nexport async function selectedPair(pc: PeerConnectionLike): Promise<SelectedPair | null> {\n const reports = await statsOf(pc);\n if (!reports) return null;\n const pair = selectedCandidatePairReport(reports);\n if (!pair?.localCandidateId || !pair.remoteCandidateId) return null;\n const byId = new Map(reports.map((report) => [report.id, report]));\n const localType = byId.get(pair.localCandidateId)?.candidateType;\n const remoteType = byId.get(pair.remoteCandidateId)?.candidateType;\n if (!localType || !remoteType) return null;\n return { localId: pair.localCandidateId, localType, remoteType };\n}\n\n/** IDs of every local candidate the browser has reported so far. Null when stats are unavailable. */\nexport async function localCandidateIds(pc: PeerConnectionLike): Promise<Set<string> | null> {\n const reports = await statsOf(pc);\n if (!reports) return null;\n return new Set(reports.filter((r) => r.type === \"local-candidate\").map((r) => r.id));\n}\n\nasync function statsOf(pc: PeerConnectionLike): Promise<StatsReportLike[] | null> {\n try {\n const collection = await pc.getStats();\n const reports: StatsReportLike[] = [];\n collection.forEach((report) => reports.push(report));\n return reports;\n } catch {\n return null;\n }\n}\n\nexport type PathKind = \"direct\" | \"relayed\";\n\n/**\n * How media is carried: `localPath` for this side of the selected pair, `remotePath` for the other side, and `path`\n * relayed when either side is. `remotePath` is this browser's view of the other side and may miss a remote relay.\n */\nexport type PathReport = { readonly path: PathKind; readonly localPath: PathKind; readonly remotePath: PathKind };\n\nexport function pathReport(pair: SelectedPair): PathReport {\n const localPath = kindOf(pair.localType);\n const remotePath = kindOf(pair.remoteType);\n return { path: localPath === \"relayed\" || remotePath === \"relayed\" ? \"relayed\" : \"direct\", localPath, remotePath };\n}\n\nexport function samePath(a: PathReport, b: PathReport): boolean {\n return a.path === b.path && a.localPath === b.localPath && a.remotePath === b.remotePath;\n}\n\nfunction kindOf(candidateType: string): PathKind {\n return candidateType === \"relay\" ? \"relayed\" : \"direct\";\n}\n", "import { MAX_QUALITY_CODEC_LENGTH } from \"@strevokit/signaling-protocol\";\nimport type { PeerConnectionLike, StatsReportLike } from \"./env.js\";\nimport { type PathReport, pathReport, selectedCandidatePairReport, selectedPair } from \"./selectedPair.js\";\n\n/** Default interval between quality samples while a peer link is alive. */\nexport const QUALITY_SAMPLE_INTERVAL_MS = 5_000;\n\n/** Bounded media-health snapshot for one peer link. Never includes SDP, IPs, or raw stats dumps. */\nexport type QualitySample = {\n readonly connectionState: string;\n readonly sampledAt: string;\n readonly rttMs?: number;\n readonly jitterMs?: number;\n readonly packetLossRatio?: number;\n readonly sendBitrateBps?: number;\n readonly receiveBitrateBps?: number;\n readonly iceLocalCandidateType?: string;\n readonly iceRemoteCandidateType?: string;\n readonly path?: PathReport[\"path\"];\n readonly localPath?: PathReport[\"localPath\"];\n readonly remotePath?: PathReport[\"remotePath\"];\n readonly framesDropped?: number;\n readonly frameWidth?: number;\n readonly frameHeight?: number;\n readonly codec?: string;\n readonly incomplete?: boolean;\n};\n\n/** Byte counters from the previous sample, used to derive bitrates. */\nexport type QualityByteSnapshot = {\n readonly sent: number;\n readonly received: number;\n readonly atMs: number;\n};\n\nexport type QualitySampleResult = {\n readonly sample: QualitySample;\n readonly bytes: QualityByteSnapshot | null;\n};\n\nfunction finiteNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction nonNegative(value: number | undefined): number | undefined {\n return value !== undefined && value >= 0 ? value : undefined;\n}\n\nasync function collectReports(pc: PeerConnectionLike): Promise<StatsReportLike[] | null> {\n try {\n const collection = await pc.getStats();\n const reports: StatsReportLike[] = [];\n collection.forEach((report) => reports.push(report));\n return reports;\n } catch {\n return null;\n }\n}\n\nfunction pickRttMs(reports: StatsReportLike[]): number | undefined {\n const pair = selectedCandidatePairReport(reports);\n if (pair) {\n const seconds = finiteNumber(pair.currentRoundTripTime);\n if (seconds !== undefined) return nonNegative(seconds * 1000);\n }\n for (const report of reports) {\n if (report.type === \"remote-inbound-rtp\") {\n const seconds = finiteNumber(report.roundTripTime);\n if (seconds !== undefined) return nonNegative(seconds * 1000);\n }\n }\n return undefined;\n}\n\nfunction pickJitterMs(reports: StatsReportLike[]): number | undefined {\n for (const report of reports) {\n if (report.type === \"inbound-rtp\") {\n const seconds = finiteNumber(report.jitter);\n if (seconds !== undefined) return nonNegative(seconds * 1000);\n }\n }\n return undefined;\n}\n\nfunction pickPacketLossRatio(reports: StatsReportLike[]): number | undefined {\n let lost = 0;\n let received = 0;\n let saw = false;\n for (const report of reports) {\n if (report.type !== \"inbound-rtp\") continue;\n const packetsLost = finiteNumber(report.packetsLost);\n const packetsReceived = finiteNumber(report.packetsReceived);\n if (packetsLost === undefined && packetsReceived === undefined) continue;\n saw = true;\n lost += Math.max(0, packetsLost ?? 0);\n received += Math.max(0, packetsReceived ?? 0);\n }\n if (!saw) return undefined;\n const total = lost + received;\n if (total <= 0) return 0;\n return Math.min(1, lost / total);\n}\n\nfunction sumBytes(reports: StatsReportLike[], type: \"outbound-rtp\" | \"inbound-rtp\", key: \"bytesSent\" | \"bytesReceived\"): number | undefined {\n let total = 0;\n let saw = false;\n for (const report of reports) {\n if (report.type !== type) continue;\n const value = finiteNumber(report[key]);\n if (value === undefined) continue;\n saw = true;\n total += Math.max(0, value);\n }\n return saw ? total : undefined;\n}\n\nfunction bitrateBps(current: number | undefined, previous: number | undefined, elapsedMs: number): number | undefined {\n if (current === undefined || previous === undefined || elapsedMs <= 0) return undefined;\n const delta = current - previous;\n if (delta < 0) return undefined;\n return (delta * 8 * 1000) / elapsedMs;\n}\n\nfunction pickFrames(reports: StatsReportLike[]): {\n framesDropped?: number;\n frameWidth?: number;\n frameHeight?: number;\n} {\n let framesDropped: number | undefined;\n let frameWidth: number | undefined;\n let frameHeight: number | undefined;\n for (const report of reports) {\n if (report.type !== \"inbound-rtp\") continue;\n const dropped = finiteNumber(report.framesDropped);\n if (dropped !== undefined && dropped >= 0) framesDropped = (framesDropped ?? 0) + dropped;\n const width = finiteNumber(report.frameWidth);\n const height = finiteNumber(report.frameHeight);\n if (width !== undefined && width > 0) frameWidth = width;\n if (height !== undefined && height > 0) frameHeight = height;\n }\n return {\n ...(framesDropped !== undefined ? { framesDropped } : {}),\n ...(frameWidth !== undefined ? { frameWidth } : {}),\n ...(frameHeight !== undefined ? { frameHeight } : {}),\n };\n}\n\n/** Mirrors signaling-protocol optionalShortToken rules so invalid codecs are omitted, not sent. */\nfunction isQualityCodecToken(value: string): boolean {\n if (value.length === 0 || value.length > MAX_QUALITY_CODEC_LENGTH) return false;\n if (value.includes(\"\\r\\n\") || value.includes(\"\\n\")) return false;\n if (/^v=0/i.test(value) || /^candidate:/i.test(value) || /\\bIN IP[46]\\b/i.test(value)) return false;\n return /^[a-zA-Z0-9._+/-]+$/.test(value);\n}\n\nfunction pickCodec(reports: StatsReportLike[]): string | undefined {\n const byId = new Map(reports.map((report) => [report.id, report]));\n for (const report of reports) {\n if (report.type !== \"inbound-rtp\" && report.type !== \"outbound-rtp\") continue;\n const codecId = report.codecId;\n if (!codecId) continue;\n const mime = byId.get(codecId)?.mimeType;\n if (typeof mime === \"string\" && isQualityCodecToken(mime)) return mime;\n }\n for (const report of reports) {\n if (report.type === \"codec\" && typeof report.mimeType === \"string\" && isQualityCodecToken(report.mimeType)) {\n return report.mimeType;\n }\n }\n return undefined;\n}\n\n/**\n * Builds one quality sample from `getStats`. Never throws: missing or failing stats yield an incomplete sample.\n */\nexport async function buildQualitySample(\n pc: PeerConnectionLike,\n options: {\n connectionState: string;\n nowMs: number;\n previous?: QualityByteSnapshot | null;\n },\n): Promise<QualitySampleResult> {\n const sampledAt = new Date(options.nowMs).toISOString();\n const base: QualitySample = {\n connectionState: options.connectionState,\n sampledAt,\n };\n\n const reports = await collectReports(pc);\n if (!reports) {\n return { sample: { ...base, incomplete: true }, bytes: options.previous ?? null };\n }\n\n const rttMs = pickRttMs(reports);\n const jitterMs = pickJitterMs(reports);\n const packetLossRatio = pickPacketLossRatio(reports);\n const bytesSent = sumBytes(reports, \"outbound-rtp\", \"bytesSent\");\n const bytesReceived = sumBytes(reports, \"inbound-rtp\", \"bytesReceived\");\n const elapsedMs = options.previous ? Math.max(0, options.nowMs - options.previous.atMs) : 0;\n const sendBitrateBps = bitrateBps(bytesSent, options.previous?.sent, elapsedMs);\n const receiveBitrateBps = bitrateBps(bytesReceived, options.previous?.received, elapsedMs);\n const frames = pickFrames(reports);\n const codec = pickCodec(reports);\n\n let iceLocalCandidateType: string | undefined;\n let iceRemoteCandidateType: string | undefined;\n let pathFields: Partial<PathReport> = {};\n try {\n const pair = await selectedPair(pc);\n if (pair) {\n iceLocalCandidateType = pair.localType;\n iceRemoteCandidateType = pair.remoteType;\n pathFields = pathReport(pair);\n }\n } catch {\n // Path helpers must not fail the sample.\n }\n\n const hasCore =\n rttMs !== undefined &&\n jitterMs !== undefined &&\n packetLossRatio !== undefined &&\n sendBitrateBps !== undefined &&\n receiveBitrateBps !== undefined &&\n iceLocalCandidateType !== undefined;\n\n const sample: QualitySample = {\n ...base,\n ...(rttMs !== undefined ? { rttMs } : {}),\n ...(jitterMs !== undefined ? { jitterMs } : {}),\n ...(packetLossRatio !== undefined ? { packetLossRatio } : {}),\n ...(sendBitrateBps !== undefined ? { sendBitrateBps } : {}),\n ...(receiveBitrateBps !== undefined ? { receiveBitrateBps } : {}),\n ...(iceLocalCandidateType ? { iceLocalCandidateType } : {}),\n ...(iceRemoteCandidateType ? { iceRemoteCandidateType } : {}),\n ...pathFields,\n ...frames,\n ...(codec ? { codec } : {}),\n ...(!hasCore ? { incomplete: true } : {}),\n };\n\n const bytes: QualityByteSnapshot | null =\n bytesSent !== undefined || bytesReceived !== undefined\n ? {\n sent: bytesSent ?? options.previous?.sent ?? 0,\n received: bytesReceived ?? options.previous?.received ?? 0,\n atMs: options.nowMs,\n }\n : (options.previous ?? null);\n\n return { sample, bytes };\n}\n", "import type { IceCandidate, IceServerConfig, PayloadMap } from \"@strevokit/signaling-protocol\";\nimport type { EngineEnvironment, IceCandidateLike, PeerConnectionLike, TimerHandle, TransceiverLike } from \"./env.js\";\nimport type { Log } from \"./log.js\";\nimport {\n QUALITY_SAMPLE_INTERVAL_MS,\n type QualityByteSnapshot,\n type QualitySample,\n buildQualitySample,\n} from \"./qualitySample.js\";\nimport { localCandidateIds, type PathReport, pathReport, samePath, selectedPair } from \"./selectedPair.js\";\n\nexport { QUALITY_SAMPLE_INTERVAL_MS } from \"./qualitySample.js\";\n\n/** Remote candidates kept until the remote description is set. */\nexport const MAX_QUEUED_CANDIDATES = 64;\n/** Time allowed from link creation to the first `connected`. */\nexport const CONNECTION_TIMEOUT_MS = 30_000;\n/** Time allowed for one switch attempt to select a fresh local candidate. */\nexport const SWITCH_ATTEMPT_TIMEOUT_MS = 15_000;\n/** Delays before retrying a failed switch attempt; the last one repeats. */\nexport const SWITCH_RETRY_DELAYS_MS: readonly number[] = [5_000, 10_000, 20_000];\n/** No switch attempt starts closer than this to the expiry of the credentials in use. */\nexport const SWITCH_DEADLINE_MARGIN_MS = 5_000;\n/** How often a switch attempt checks the selected candidate pair. */\nexport const SWITCH_POLL_MS = 1_000;\n/** How long a drop during a switch is held back before it is reported as an interruption. */\nexport const SWITCH_INTERRUPTION_HOLD_MS = 5_000;\n/** How long an interruption must last before an offerer restarts the connection; shorter drops often heal alone. */\nexport const INTERRUPTION_RESTART_HOLD_MS = 3_000;\n/** Times after each path report at which the path is read again; after the last, every `PATH_RECHECK_INTERVAL_MS`. */\nexport const PATH_RECHECK_DELAYS_MS: readonly number[] = [2_000, 10_000];\nexport const PATH_RECHECK_INTERVAL_MS = 30_000;\n\nexport type LinkRole = \"offerer\" | \"answerer\";\nexport type LinkStatus = \"connecting\" | \"connected\" | \"interrupted\" | \"failed\";\nexport type LinkFailure = \"connection_failed\" | \"connection_timeout\" | \"negotiation_failed\";\nexport type LocalKind = \"camera\" | \"microphone\" | \"screen\";\nexport type SwitchOutcome = \"completed\" | \"failed\";\nexport type RestartReason = \"refresh\" | \"peer_request\" | \"interruption\" | \"network\";\n/** What started an attempt to restore media that dropped while the meeting connection stayed up. */\nexport type RecoveryTrigger = \"media\" | \"network\";\nexport interface PeerLinkCallbacks {\n sendOffer(payload: PayloadMap[\"webrtc.offer\"]): void;\n sendAnswer(payload: PayloadMap[\"webrtc.answer\"]): void;\n sendCandidate(payload: PayloadMap[\"webrtc.ice_candidate\"]): void;\n /** Called on every status transition after `connecting`. `reason` is set for `failed`. */\n statusChanged(status: LinkStatus, reason?: LinkFailure): void;\n /**\n * How media is carried on each side, after each move to `connected` and after each completed switch. Null when the\n * browser reports no stats.\n */\n connected(report: PathReport | null): void;\n /** Answerer: asks the other side to restart. False when it cannot be asked, which ends the switch. */\n requestRestart(): boolean;\n /** The outcome of one switch attempt. */\n switchResult(outcome: SwitchOutcome): void;\n /** An offerer started a restart because the connection was interrupted. */\n recoveryStarted(trigger: RecoveryTrigger): void;\n /** Periodic media-health sample while the link is alive. Failures must not tear down media. */\n qualitySample(sample: QualitySample): void;\n}\n\n/** A planned move to fresh candidates before the credentials in use expire. */\ntype SwitchState = {\n deadline: number;\n attempt: number;\n /** Local candidate IDs known before the current attempt; the attempt completes on a pair outside this set. */\n known: ReadonlySet<string>;\n attemptTimer: TimerHandle | undefined;\n pollTimer: TimerHandle | undefined;\n retryTimer: TimerHandle | undefined;\n};\n\nfunction copyServers(iceServers: readonly IceServerConfig[]): { urls: string[]; username?: string; credential?: string }[] {\n return iceServers.map((server) => {\n const copy: { urls: string[]; username?: string; credential?: string } = { urls: [...server.urls] };\n if (server.username !== undefined) copy.username = server.username;\n if (server.credential !== undefined) copy.credential = server.credential;\n return copy;\n });\n}\n\nexport interface PeerLinkOptions {\n env: EngineEnvironment;\n log: Log;\n selfId: string;\n peerId: string;\n iceServers: readonly IceServerConfig[];\n timeoutMs: number;\n local: Readonly<Record<LocalKind, MediaStreamTrack | null>>;\n callbacks: PeerLinkCallbacks;\n}\n\nexport function roleFor(selfId: string, peerId: string): LinkRole {\n return selfId < peerId ? \"offerer\" : \"answerer\";\n}\n\n/** Maps `connectionState`, or `iceConnectionState` where the former is unavailable, to a coarse state. */\nfunction coarseState(pc: PeerConnectionLike): \"connecting\" | \"connected\" | \"disconnected\" | \"failed\" | \"closed\" {\n const state = typeof pc.connectionState === \"string\" ? pc.connectionState : pc.iceConnectionState;\n switch (state) {\n case \"connected\":\n case \"completed\":\n return \"connected\";\n case \"disconnected\":\n return \"disconnected\";\n case \"failed\":\n return \"failed\";\n case \"closed\":\n return \"closed\";\n default:\n return \"connecting\";\n }\n}\n\n/** One media connection to one other participant, with fixed audio, camera, and screen transceivers. */\nexport class PeerLink {\n readonly peerId: string;\n readonly role: LinkRole;\n #status: LinkStatus = \"connecting\";\n #everConnected = false;\n #closed = false;\n readonly #pc: PeerConnectionLike;\n readonly #env: EngineEnvironment;\n readonly #log: Log;\n readonly #callbacks: PeerLinkCallbacks;\n readonly #local: Record<LocalKind, MediaStreamTrack | null>;\n readonly #queued: IceCandidateLike[] = [];\n #remoteDescriptionSet = false;\n #offerHandled = false;\n #audio: TransceiverLike | undefined;\n #video: TransceiverLike | undefined;\n #screen: TransceiverLike | undefined;\n #streams: Record<LocalKind, MediaStream | null> = { camera: null, microphone: null, screen: null };\n #timer: TimerHandle | undefined;\n #chain: Promise<void> = Promise.resolve();\n #switch: SwitchState | null = null;\n #restarting = false;\n #interruptionHold: TimerHandle | undefined;\n #restartHold: TimerHandle | undefined;\n /** Local candidates gathered while an offer is being created; sent after it so the other side sees it first. */\n #heldLocal: IceCandidate[] | null = null;\n #remoteOrigin: string | undefined;\n #lastReport: PathReport | null = null;\n #recheckTimer: TimerHandle | undefined;\n #qualityTimer: TimerHandle | undefined;\n #qualityBytes: QualityByteSnapshot | null = null;\n #qualityRunning = false;\n\n /** Throws when the connection cannot be created; the owner treats that as a negotiation failure. */\n constructor(options: PeerLinkOptions) {\n this.peerId = options.peerId;\n this.role = roleFor(options.selfId, options.peerId);\n this.#env = options.env;\n this.#log = options.log;\n this.#callbacks = options.callbacks;\n const { local } = options;\n this.#local = { ...local };\n const PeerConnection = options.env.RTCPeerConnection;\n if (!PeerConnection) throw new Error(\"peer connections unavailable\");\n this.#pc = new PeerConnection({ iceServers: copyServers(options.iceServers) });\n this.#pc.onicecandidate = (event) => this.#onLocalCandidate(event.candidate);\n this.#pc.onconnectionstatechange = () => this.#onStateChange();\n this.#pc.oniceconnectionstatechange = () => {\n if (typeof this.#pc.connectionState !== \"string\") this.#onStateChange();\n };\n this.#timer = this.#env.setTimeout(() => {\n this.#timer = undefined;\n if (!this.#everConnected) this.#fail(\"connection_timeout\");\n }, options.timeoutMs);\n this.#log.debug(\"media connection created\", { participantId: this.peerId });\n }\n\n get status(): LinkStatus {\n return this.#status;\n }\n\n get everConnected(): boolean {\n return this.#everConnected;\n }\n\n /** Usable for rendering: connected at least once, and neither failed nor closed. */\n get usable(): boolean {\n return this.#everConnected && !this.#closed && this.#status !== \"failed\";\n }\n\n /** The stable stream holding the received track of `kind`, once the transceivers exist. */\n remoteStream(kind: LocalKind): MediaStream | null {\n return this.#streams[kind];\n }\n\n /** Offerer: adds the transceivers (audio \u2192 camera \u2192 screen) and sends the offer. */\n start(): void {\n if (this.role !== \"offerer\" || this.#audio) return;\n this.#run(async () => {\n this.#audio = this.#pc.addTransceiver(\"audio\", { direction: \"sendrecv\" });\n this.#video = this.#pc.addTransceiver(\"video\", { direction: \"sendrecv\" });\n this.#screen = this.#pc.addTransceiver(\"video\", { direction: \"sendrecv\" });\n this.#bindStreams();\n await this.#attachAll();\n const offer = await this.#pc.createOffer();\n this.#heldLocal = [];\n await this.#pc.setLocalDescription(offer);\n if (this.#closed) return;\n this.#sendOffer(this.#pc.localDescription?.sdp ?? offer.sdp ?? \"\");\n });\n }\n\n /**\n * Answerer: applies the offer and sends the answer. A later offer (a restart) is answered when no other\n * negotiation is in progress, keeping the transceivers and received streams; otherwise it is ignored.\n */\n acceptOffer(sdp: string): void {\n if (this.role !== \"answerer\") {\n this.#log.debug(\"frame ignored\", { messageType: \"webrtc.offer\", reason: \"offerer\" });\n return;\n }\n if (this.#offerHandled) {\n this.#acceptReoffer(sdp);\n return;\n }\n this.#offerHandled = true;\n this.#remoteOrigin = sessionOrigin(sdp);\n this.#run(async () => {\n await this.#pc.setRemoteDescription({ type: \"offer\", sdp });\n this.#remoteDescriptionSet = true;\n // Bind by index: two video m-lines cannot be told apart by track.kind alone.\n const transceivers = this.#pc.getTransceivers();\n this.#audio = transceivers[0];\n this.#video = transceivers[1];\n this.#screen = transceivers[2];\n for (const transceiver of [this.#audio, this.#video, this.#screen]) {\n if (transceiver) transceiver.direction = \"sendrecv\";\n }\n this.#bindStreams();\n await this.#attachAll();\n const answer = await this.#pc.createAnswer();\n await this.#pc.setLocalDescription(answer);\n if (this.#closed) return;\n const answerSdp = this.#pc.localDescription?.sdp ?? answer.sdp ?? \"\";\n this.#callbacks.sendAnswer({ targetParticipantId: this.peerId, sdp: answerSdp });\n await this.#flushCandidates();\n });\n }\n\n /**\n * Answerer: whether `sdp` comes from a different connection than the one this link answered first, as when the\n * other side replaced its connection. Such an offer needs a fresh link; a restart keeps the same origin.\n */\n startsNewSession(sdp: string): boolean {\n const origin = sessionOrigin(sdp);\n return this.#offerHandled && this.#remoteOrigin !== undefined && origin !== undefined && origin !== this.#remoteOrigin;\n }\n\n /** Offerer: applies the answer while an offer is outstanding; otherwise ignored. */\n acceptAnswer(sdp: string): void {\n if (this.role !== \"offerer\") {\n this.#log.debug(\"frame ignored\", { messageType: \"webrtc.answer\", reason: \"answerer\" });\n return;\n }\n this.#run(async () => {\n if (this.#pc.signalingState !== \"have-local-offer\") {\n this.#log.debug(\"frame ignored\", { messageType: \"webrtc.answer\", state: this.#pc.signalingState });\n return;\n }\n if (!this.#restarting) {\n await this.#pc.setRemoteDescription({ type: \"answer\", sdp });\n this.#remoteDescriptionSet = true;\n await this.#flushCandidates();\n return;\n }\n this.#restarting = false;\n try {\n await this.#pc.setRemoteDescription({ type: \"answer\", sdp });\n } catch {\n this.#restartFailed();\n this.#rescheduleInterruptionRestartIfNeeded();\n return;\n }\n await this.#flushCandidates();\n this.#rescheduleInterruptionRestartIfNeeded();\n });\n }\n\n /** Replaces the server list for future candidate gathering. The current connection is untouched. */\n updateIceServers(iceServers: readonly IceServerConfig[]): void {\n if (this.#closed) return;\n try {\n this.#pc.setConfiguration({ ...this.#pc.getConfiguration(), iceServers: copyServers(iceServers) });\n } catch {\n this.#log.debug(\"negotiation step failed\", { participantId: this.peerId, reason: \"configuration\" });\n }\n }\n\n /**\n * Moves to fresh candidates before `deadlineMs` when this side sends through a relay, whose allocation is tied to\n * the credentials about to expire. Direct paths and links that are not connected are left alone.\n */\n planSwitch(deadlineMs: number): void {\n if (this.#closed || this.#status !== \"connected\") return;\n const active = this.#switch;\n if (active) {\n active.deadline = deadlineMs;\n return;\n }\n void selectedPair(this.#pc).then((pair) => {\n if (this.#closed || this.#switch || this.#status !== \"connected\" || pair?.localType !== \"relay\") return;\n const started: SwitchState = {\n deadline: deadlineMs,\n attempt: 0,\n known: new Set(),\n attemptTimer: undefined,\n pollTimer: undefined,\n retryTimer: undefined,\n };\n this.#switch = started;\n this.#log.debug(\"media path switch started\", { participantId: this.peerId });\n void this.#beginAttempt(started);\n });\n }\n\n /**\n * Offerer: renegotiates with fresh candidates. Joins a restart whose answer is still outstanding instead of starting\n * another; a restart that already finished may predate the other side's fresh servers, so it does not count.\n */\n restart(reason: RestartReason): boolean {\n if (this.role !== \"offerer\" || this.#closed || this.#restarting) return false;\n this.#restarting = true;\n this.#log.debug(\"media connection restart\", { participantId: this.peerId, reason });\n this.#chain = this.#chain.then(async () => {\n if (this.#closed) return;\n try {\n const offer = await this.#pc.createOffer({ iceRestart: true });\n this.#heldLocal = [];\n await this.#pc.setLocalDescription(offer);\n if (this.#closed) return;\n this.#sendOffer(this.#pc.localDescription?.sdp ?? offer.sdp ?? \"\");\n } catch {\n this.#heldLocal = null;\n this.#restarting = false;\n this.#restartFailed();\n this.#rescheduleInterruptionRestartIfNeeded();\n }\n });\n return true;\n }\n\n /** The browser regained connectivity or changed networks: an offerer restarts an interrupted connection now. */\n networkChanged(): void {\n if (this.#closed || this.#status !== \"interrupted\" || this.role !== \"offerer\") return;\n this.#clearRestartHold();\n if (this.restart(\"network\")) this.#callbacks.recoveryStarted(\"network\");\n else this.#scheduleInterruptionRestart();\n }\n\n addRemoteCandidate(candidate: IceCandidate): void {\n if (this.#closed) return;\n const copy: IceCandidateLike = {\n candidate: candidate.candidate,\n sdpMid: candidate.sdpMid,\n sdpMLineIndex: candidate.sdpMLineIndex,\n ...(candidate.usernameFragment !== undefined ? { usernameFragment: candidate.usernameFragment } : {}),\n };\n if (!this.#remoteDescriptionSet) {\n if (this.#queued.length >= MAX_QUEUED_CANDIDATES) {\n this.#log.debug(\"late candidate dropped\", { participantId: this.peerId, reason: \"queue_full\" });\n return;\n }\n this.#queued.push(copy);\n return;\n }\n void this.#applyCandidate(copy);\n }\n\n /** Sends `track` for `kind` (or nothing for `null`). Skipped when that track is already being sent. */\n attachLocal(kind: LocalKind, track: MediaStreamTrack | null): void {\n this.#local[kind] = track;\n if (this.#closed) return;\n const sender =\n (kind === \"camera\" ? this.#video : kind === \"screen\" ? this.#screen : this.#audio)?.sender;\n if (!sender || sender.track === track) return;\n sender.replaceTrack(track).catch(() => {\n this.#log.debug(\"negotiation step failed\", { participantId: this.peerId, kind, reason: \"replace_track\" });\n });\n }\n\n /** Closes the connection and stops received tracks. Reports nothing. Idempotent. */\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.#clearTimer();\n this.#endSwitch();\n this.#clearInterruptionHold();\n this.#clearRestartHold();\n this.#clearRecheck();\n this.#stopQualitySampler();\n this.#pc.onicecandidate = null;\n this.#pc.onconnectionstatechange = null;\n this.#pc.oniceconnectionstatechange = null;\n for (const stream of Object.values(this.#streams)) {\n for (const track of stream?.getTracks() ?? []) {\n try {\n track.stop();\n } catch {\n // Already stopped.\n }\n }\n }\n try {\n this.#pc.close();\n } catch {\n // Already closed.\n }\n }\n\n #run(step: () => Promise<void>): void {\n this.#chain = this.#chain.then(async () => {\n if (this.#closed) return;\n try {\n await step();\n } catch {\n if (this.#closed) return;\n this.#log.debug(\"negotiation step failed\", { participantId: this.peerId });\n this.#fail(\"negotiation_failed\");\n }\n });\n }\n\n #acceptReoffer(sdp: string): void {\n this.#run(async () => {\n if (this.#pc.signalingState !== \"stable\") {\n this.#log.debug(\"frame ignored\", { messageType: \"webrtc.offer\", reason: \"busy\" });\n return;\n }\n try {\n await this.#pc.setRemoteDescription({ type: \"offer\", sdp });\n const answer = await this.#pc.createAnswer();\n await this.#pc.setLocalDescription(answer);\n if (this.#closed) return;\n const answerSdp = this.#pc.localDescription?.sdp ?? answer.sdp ?? \"\";\n this.#callbacks.sendAnswer({ targetParticipantId: this.peerId, sdp: answerSdp });\n } catch {\n this.#restartFailed();\n return;\n }\n await this.#flushCandidates();\n });\n }\n\n /** A restart negotiation step failed: the connection stays as it is and the current attempt, if any, fails. */\n #restartFailed(): void {\n if (this.#closed) return;\n this.#log.debug(\"negotiation step failed\", { participantId: this.peerId, reason: \"restart\" });\n const current = this.#switch;\n if (current?.attemptTimer !== undefined) this.#attemptFailed(current);\n }\n\n async #beginAttempt(current: SwitchState): Promise<void> {\n current.attempt += 1;\n const known = await localCandidateIds(this.#pc);\n if (this.#switch !== current || this.#closed) return;\n current.known = known ?? new Set();\n current.attemptTimer = this.#env.setTimeout(() => this.#attemptFailed(current), SWITCH_ATTEMPT_TIMEOUT_MS);\n if (this.role === \"offerer\") {\n this.restart(\"refresh\");\n } else if (!this.#callbacks.requestRestart()) {\n this.#endSwitch();\n return;\n }\n this.#schedulePoll(current);\n }\n\n #schedulePoll(current: SwitchState): void {\n current.pollTimer = this.#env.setTimeout(() => {\n current.pollTimer = undefined;\n void this.#checkSwitch(current);\n }, SWITCH_POLL_MS);\n }\n\n async #checkSwitch(current: SwitchState): Promise<void> {\n if (this.#switch !== current || current.attemptTimer === undefined) return;\n const pair = coarseState(this.#pc) === \"connected\" ? await selectedPair(this.#pc) : null;\n if (this.#switch !== current || current.attemptTimer === undefined) return;\n if (!pair || current.known.has(pair.localId)) {\n this.#schedulePoll(current);\n return;\n }\n this.#endSwitch();\n const report = pathReport(pair);\n this.#log.debug(\"media path switch completed\", { participantId: this.peerId, attempt: current.attempt, ...report });\n this.#sendPath(report);\n this.#callbacks.switchResult(\"completed\");\n }\n\n #attemptFailed(current: SwitchState): void {\n if (this.#switch !== current || this.#closed) return;\n this.#clearSwitchTimers(current);\n this.#restarting = false;\n this.#rescheduleInterruptionRestartIfNeeded();\n this.#log.debug(\"media path switch attempt failed\", { participantId: this.peerId, attempt: current.attempt });\n this.#callbacks.switchResult(\"failed\");\n if (this.#switch !== current || this.#closed) return;\n const delay = SWITCH_RETRY_DELAYS_MS[Math.min(current.attempt, SWITCH_RETRY_DELAYS_MS.length) - 1] ?? 0;\n if (this.#env.now() + delay >= current.deadline - SWITCH_DEADLINE_MARGIN_MS) {\n this.#endSwitch();\n this.#log.debug(\"media path switch stopped\", { participantId: this.peerId, attempt: current.attempt });\n return;\n }\n current.retryTimer = this.#env.setTimeout(() => {\n current.retryTimer = undefined;\n if (this.#switch === current && !this.#closed) void this.#beginAttempt(current);\n }, delay);\n }\n\n #endSwitch(): void {\n const current = this.#switch;\n if (!current) return;\n this.#switch = null;\n this.#clearSwitchTimers(current);\n }\n\n #clearSwitchTimers(current: SwitchState): void {\n for (const timer of [current.attemptTimer, current.pollTimer, current.retryTimer]) {\n if (timer !== undefined) this.#env.clearTimeout(timer);\n }\n current.attemptTimer = undefined;\n current.pollTimer = undefined;\n current.retryTimer = undefined;\n }\n\n #clearInterruptionHold(): void {\n if (this.#interruptionHold !== undefined) this.#env.clearTimeout(this.#interruptionHold);\n this.#interruptionHold = undefined;\n }\n\n #clearRestartHold(): void {\n if (this.#restartHold !== undefined) this.#env.clearTimeout(this.#restartHold);\n this.#restartHold = undefined;\n }\n\n /** When a concurrent restart ends and the link is still interrupted, arm another recovery. */\n #rescheduleInterruptionRestartIfNeeded(): void {\n if (this.#closed || this.#status !== \"interrupted\") return;\n this.#scheduleInterruptionRestart();\n }\n\n /** Offerer: restarts the connection when an interruption outlasts the hold, without waiting for it to fail. */\n #scheduleInterruptionRestart(): void {\n if (this.role !== \"offerer\" || this.#restartHold !== undefined) return;\n this.#restartHold = this.#env.setTimeout(() => {\n this.#restartHold = undefined;\n if (this.#closed || this.#status !== \"interrupted\" || coarseState(this.#pc) === \"connected\") return;\n if (this.restart(\"interruption\")) this.#callbacks.recoveryStarted(\"media\");\n else this.#scheduleInterruptionRestart();\n }, INTERRUPTION_RESTART_HOLD_MS);\n }\n\n #bindStreams(): void {\n const Stream = this.#env.MediaStream;\n if (!Stream) return;\n if (this.#audio && !this.#streams.microphone) this.#streams.microphone = new Stream([this.#audio.receiver.track]);\n if (this.#video && !this.#streams.camera) this.#streams.camera = new Stream([this.#video.receiver.track]);\n if (this.#screen && !this.#streams.screen) this.#streams.screen = new Stream([this.#screen.receiver.track]);\n }\n\n async #attachAll(): Promise<void> {\n const pairs: [TransceiverLike | undefined, MediaStreamTrack | null][] = [\n [this.#audio, this.#local.microphone],\n [this.#video, this.#local.camera],\n [this.#screen, this.#local.screen],\n ];\n for (const [transceiver, track] of pairs) {\n if (transceiver && track && transceiver.sender.track !== track) await transceiver.sender.replaceTrack(track);\n }\n }\n\n async #flushCandidates(): Promise<void> {\n for (const candidate of this.#queued.splice(0)) await this.#applyCandidate(candidate);\n }\n\n async #applyCandidate(candidate: IceCandidateLike): Promise<void> {\n if (this.#closed) return;\n try {\n await this.#pc.addIceCandidate(candidate);\n } catch {\n this.#log.debug(\"late candidate dropped\", { participantId: this.peerId, reason: \"rejected\" });\n }\n }\n\n /** Sends the offer, then the candidates gathered while it was being created. */\n #sendOffer(sdp: string): void {\n this.#callbacks.sendOffer({ targetParticipantId: this.peerId, sdp });\n const held = this.#heldLocal;\n this.#heldLocal = null;\n for (const candidate of held ?? []) this.#callbacks.sendCandidate({ targetParticipantId: this.peerId, candidate });\n }\n\n #onLocalCandidate(candidate: IceCandidateLike | null): void {\n if (this.#closed || !candidate || candidate.candidate === \"\") return;\n const payload: IceCandidate = {\n candidate: candidate.candidate,\n sdpMid: candidate.sdpMid ?? null,\n sdpMLineIndex: candidate.sdpMLineIndex ?? null,\n };\n if (typeof candidate.usernameFragment === \"string\") payload.usernameFragment = candidate.usernameFragment;\n if (this.#heldLocal) this.#heldLocal.push(payload);\n else this.#callbacks.sendCandidate({ targetParticipantId: this.peerId, candidate: payload });\n }\n\n #onStateChange(): void {\n if (this.#closed || this.#status === \"failed\") return;\n const state = coarseState(this.#pc);\n this.#log.debug(\"media connection state\", { participantId: this.peerId, state });\n switch (state) {\n case \"connected\":\n this.#everConnected = true;\n this.#clearTimer();\n this.#clearInterruptionHold();\n if (this.#setStatus(\"connected\")) void this.#reportPath();\n this.#startQualitySampler();\n return;\n case \"disconnected\":\n if (!this.#everConnected) return;\n if (this.#switch && this.#status === \"connected\") {\n // Moving to fresh candidates can drop the connection briefly; report it only if it lasts.\n this.#interruptionHold ??= this.#env.setTimeout(() => {\n this.#interruptionHold = undefined;\n if (!this.#closed && coarseState(this.#pc) !== \"connected\") this.#setStatus(\"interrupted\");\n }, SWITCH_INTERRUPTION_HOLD_MS);\n return;\n }\n this.#setStatus(\"interrupted\");\n this.#startQualitySampler();\n return;\n case \"failed\":\n this.#fail(\"connection_failed\");\n return;\n default:\n return;\n }\n }\n\n #qualityConnectionState(): string {\n if (this.#status === \"interrupted\") return \"interrupted\";\n if (this.#status === \"failed\") return \"failed\";\n if (this.#status === \"connected\") return \"connected\";\n return \"connecting\";\n }\n\n #startQualitySampler(): void {\n if (this.#closed || this.#qualityRunning) return;\n this.#qualityRunning = true;\n this.#scheduleQualitySample();\n }\n\n #stopQualitySampler(): void {\n this.#qualityRunning = false;\n if (this.#qualityTimer !== undefined) this.#env.clearTimeout(this.#qualityTimer);\n this.#qualityTimer = undefined;\n this.#qualityBytes = null;\n }\n\n #scheduleQualitySample(): void {\n if (!this.#qualityRunning || this.#closed) return;\n this.#qualityTimer = this.#env.setTimeout(() => {\n this.#qualityTimer = undefined;\n void this.#emitQualitySample();\n }, QUALITY_SAMPLE_INTERVAL_MS);\n }\n\n async #emitQualitySample(): Promise<void> {\n if (!this.#qualityRunning || this.#closed) return;\n try {\n const result = await buildQualitySample(this.#pc, {\n connectionState: this.#qualityConnectionState(),\n nowMs: this.#env.now(),\n previous: this.#qualityBytes,\n });\n if (this.#closed || !this.#qualityRunning) return;\n this.#qualityBytes = result.bytes;\n this.#log.debug(\"webrtc quality sample\", {\n participantId: this.peerId,\n state: result.sample.connectionState,\n ...(result.sample.rttMs !== undefined ? { rttMs: result.sample.rttMs } : {}),\n ...(result.sample.jitterMs !== undefined ? { jitterMs: result.sample.jitterMs } : {}),\n ...(result.sample.packetLossRatio !== undefined ? { packetLossRatio: result.sample.packetLossRatio } : {}),\n ...(result.sample.sendBitrateBps !== undefined ? { sendBitrateBps: result.sample.sendBitrateBps } : {}),\n ...(result.sample.receiveBitrateBps !== undefined\n ? { receiveBitrateBps: result.sample.receiveBitrateBps }\n : {}),\n ...(result.sample.path ? { path: result.sample.path } : {}),\n ...(result.sample.incomplete ? { incomplete: true } : {}),\n });\n this.#callbacks.qualitySample(result.sample);\n } catch {\n // Sampling must never affect media.\n }\n if (!this.#closed && this.#qualityRunning) this.#scheduleQualitySample();\n }\n\n /** True when the status changed. */\n #setStatus(status: LinkStatus, reason?: LinkFailure): boolean {\n if (this.#status === status) return false;\n this.#status = status;\n if (status === \"interrupted\") this.#scheduleInterruptionRestart();\n else this.#clearRestartHold();\n this.#callbacks.statusChanged(status, reason);\n return true;\n }\n\n async #reportPath(): Promise<void> {\n const pair = await selectedPair(this.#pc);\n if (this.#closed || this.#status !== \"connected\") return;\n this.#sendPath(pair ? pathReport(pair) : null);\n }\n\n /** Reports the path and reads it again on the recheck schedule, starting over from the first delay. */\n #sendPath(report: PathReport | null): void {\n this.#lastReport = report;\n this.#callbacks.connected(report);\n this.#scheduleRecheck(0);\n }\n\n #scheduleRecheck(step: number): void {\n this.#clearRecheck();\n const at = PATH_RECHECK_DELAYS_MS[step];\n const delay = at === undefined ? PATH_RECHECK_INTERVAL_MS : at - (PATH_RECHECK_DELAYS_MS[step - 1] ?? 0);\n this.#recheckTimer = this.#env.setTimeout(() => {\n this.#recheckTimer = undefined;\n void this.#recheckPath(step);\n }, delay);\n }\n\n /**\n * The browser can settle on the path after the first report, for example when the other side's relay address was\n * first seen as peer-reflexive. A changed path is reported again; an unchanged one sends nothing.\n */\n async #recheckPath(step: number): Promise<void> {\n if (this.#closed) return;\n const idle = () => this.#status !== \"connected\" || this.#switch !== null;\n const pair = idle() ? null : await selectedPair(this.#pc);\n if (this.#closed || this.#recheckTimer !== undefined) return;\n const report = pair && !idle() ? pathReport(pair) : null;\n if (report && !(this.#lastReport && samePath(report, this.#lastReport))) {\n this.#log.debug(\"media path updated\", { participantId: this.peerId, ...report });\n this.#sendPath(report);\n return;\n }\n this.#scheduleRecheck(step + 1);\n }\n\n #clearRecheck(): void {\n if (this.#recheckTimer !== undefined) this.#env.clearTimeout(this.#recheckTimer);\n this.#recheckTimer = undefined;\n }\n\n #fail(reason: LinkFailure): void {\n if (this.#closed || this.#status === \"failed\") return;\n this.#clearTimer();\n this.#setStatus(\"failed\", reason);\n this.close();\n }\n\n #clearTimer(): void {\n if (this.#timer !== undefined) this.#env.clearTimeout(this.#timer);\n this.#timer = undefined;\n }\n}\n\n/** The connection identifier on the origin line of a session description; it stays the same across restarts. */\nfunction sessionOrigin(sdp: string): string | undefined {\n for (const line of sdp.split(/\\r?\\n/)) {\n if (line.startsWith(\"o=\")) return line.split(\" \")[1];\n }\n return undefined;\n}\n", "import type { MediaKind, MediaState, MeetingEventMap, RemoteMedia } from \"../public/types.js\";\nimport { NO_REMOTE_MEDIA } from \"./mediaDelivery.js\";\n\nconst KINDS: readonly MediaKind[] = [\"camera\", \"microphone\", \"screen\"];\n\nexport type RemoteMediaInputs = {\n /** The participant is the one this side carries media with. */\n readonly active: boolean;\n /** The link connected at least once and is neither failed nor closed. */\n readonly usable: boolean;\n /** What the participant announced through signaling. */\n readonly announced: MediaState;\n readonly streams: {\n readonly camera: MediaStream | null;\n readonly microphone: MediaStream | null;\n readonly screen: MediaStream | null;\n };\n};\n\n/** A kind is available when the link to an active participant is usable and the participant announced it on. */\nexport function computeRemoteMedia(inputs: RemoteMediaInputs): RemoteMedia {\n if (!inputs.active || !inputs.usable) return NO_REMOTE_MEDIA;\n const camera = inputs.announced.camera ? inputs.streams.camera : null;\n const microphone = inputs.announced.microphone ? inputs.streams.microphone : null;\n const screen = inputs.announced.screen ? inputs.streams.screen : null;\n if (camera === null && microphone === null && screen === null) return NO_REMOTE_MEDIA;\n return Object.freeze({ camera, microphone, screen });\n}\n\ntype Emit = (event: MeetingEventMap[\"remote_media_changed\"]) => void;\n\n/** The last reported remote media per participant; reports each change of a kind exactly once. */\nexport class RemoteMediaTracker {\n readonly #current = new Map<string, RemoteMedia>();\n\n get(participantId: string): RemoteMedia {\n return this.#current.get(participantId) ?? NO_REMOTE_MEDIA;\n }\n\n update(participantId: string, next: RemoteMedia, emit: Emit): void {\n const previous = this.get(participantId);\n if (next === NO_REMOTE_MEDIA) this.#current.delete(participantId);\n else this.#current.set(participantId, next);\n for (const kind of KINDS) {\n if (previous[kind] !== next[kind]) emit({ participantId, kind, stream: next[kind] });\n }\n }\n\n /** Forgets everything without reporting. */\n clear(): void {\n this.#current.clear();\n }\n}\n", "import type { IceCandidate, IceServerConfig, SignalingMessage } from \"@strevokit/signaling-protocol\";\nimport { MeetingError } from \"../public/errors.js\";\nimport type { MediaConnectionStatus, MediaKind, MediaState, MeetingEventMap, RemoteMedia } from \"../public/types.js\";\nimport type { EngineEnvironment, TimerHandle } from \"./env.js\";\nimport type { Log } from \"./log.js\";\nimport type { DeliveryEventType, MediaDelivery, MediaDeliveryContext, PresentPeer } from \"./mediaDelivery.js\";\nimport {\n CONNECTION_TIMEOUT_MS,\n type LinkFailure,\n type LinkStatus,\n type LocalKind,\n PeerLink,\n type RecoveryTrigger,\n roleFor,\n} from \"./peerLink.js\";\nimport { DEFAULT_RECONNECT_WINDOW_MS, reconnectLogFields } from \"./reconnect/index.js\";\nimport { RemoteMediaTracker, computeRemoteMedia } from \"./remoteMedia.js\";\n\n/** How long frames from a participant not yet usable are kept. */\nexport const EARLY_FRAME_HOLD_MS = 5000;\n/** Early candidates kept per participant. */\nexport const MAX_EARLY_CANDIDATES = 64;\n\nconst NOTHING_ANNOUNCED: MediaState = Object.freeze({ camera: false, microphone: false, screen: false });\n\nexport interface PeerMediaDeliveryOptions {\n env: EngineEnvironment;\n log: Log;\n /** Not public: tests shorten the time allowed for a first connection. */\n connectionTimeoutMs?: number;\n /** How long after a connection drops it may still be replaced once. Defaults to the meeting recovery window. */\n recoveryWindowMs?: number;\n}\n\n/** Media that dropped while the meeting connection stayed up, from the first drop until it connects or is given up. */\ntype RecoveryEpisode = { startedAt: number; attempts: number; trigger: RecoveryTrigger; replaced: boolean };\n\ntype EarlyCandidates = { candidates: IceCandidate[]; timer: TimerHandle };\ntype HeldOffer = { from: string; sdp: string; timer: TimerHandle };\n\n/**\n * Carries camera, microphone, and screen directly to and from exactly one other participant. Every participant\n * applies the same rule: pair only while exactly one other participant is present, keep a pair once it has\n * connected, and report the participants it does not carry with a non-fatal error.\n */\nexport class PeerMediaDelivery implements MediaDelivery {\n readonly #env: EngineEnvironment;\n readonly #log: Log;\n readonly #timeoutMs: number;\n readonly #recoveryWindowMs: number;\n #episode: RecoveryEpisode | null = null;\n #unsubscribeNetwork: (() => void) | undefined;\n #context: MediaDeliveryContext | undefined;\n /** The latest server list; links created later use it. */\n #iceServers: readonly IceServerConfig[] = [];\n /** Whether the platform relays restart requests and results (it announces credential expiry). */\n #serverSupportsRestart = false;\n #inert = false;\n #closed = false;\n readonly #present = new Map<string, MediaState>();\n #activeId: string | null = null;\n #link: PeerLink | null = null;\n readonly #status = new Map<string, MediaConnectionStatus>();\n readonly #remote = new RemoteMediaTracker();\n readonly #limitReported = new Set<string>();\n readonly #early = new Map<string, EarlyCandidates>();\n #heldOffer: HeldOffer | undefined;\n readonly #local: Record<LocalKind, MediaStreamTrack | null> = { camera: null, microphone: null, screen: null };\n\n constructor(options: PeerMediaDeliveryOptions) {\n this.#env = options.env;\n this.#log = options.log;\n this.#timeoutMs = options.connectionTimeoutMs ?? CONNECTION_TIMEOUT_MS;\n this.#recoveryWindowMs = options.recoveryWindowMs ?? DEFAULT_RECONNECT_WINDOW_MS;\n }\n\n attach(context: MediaDeliveryContext): void {\n if (this.#closed || this.#context) return;\n this.#context = context;\n this.#iceServers = context.iceServers;\n this.#serverSupportsRestart = context.iceServersExpireAt !== null;\n if (!this.#env.RTCPeerConnection || !this.#env.MediaStream) {\n this.#inert = true;\n this.#log.warn(\"media connections unsupported\");\n this.#emit(\"error\", { error: new MeetingError(\"unsupported_environment\") });\n return;\n }\n this.#unsubscribeNetwork = this.#env.onNetworkChange?.(() => this.#link?.networkChanged());\n }\n\n peersPresent(entries: readonly PresentPeer[]): void {\n if (!this.#active()) return;\n for (const { id, media } of entries) {\n this.#present.set(id, { ...media });\n this.#limitReported.delete(id);\n }\n this.#evaluate();\n const held = this.#heldOffer;\n if (held && entries.some((entry) => entry.id === held.from)) {\n this.#dropHeldOffer();\n if (held.from === this.#activeId) this.#handleOffer(held.from, held.sdp);\n }\n }\n\n peerPresent(participantId: string, media: MediaState): void {\n this.peersPresent([{ id: participantId, media }]);\n }\n\n peerMediaChanged(participantId: string, kind: MediaKind, enabled: boolean): void {\n const announced = this.#present.get(participantId);\n if (!this.#active() || !announced) return;\n this.#present.set(participantId, { ...announced, [kind]: enabled });\n this.#refreshRemote(participantId);\n }\n\n peerLeft(participantId: string): void {\n if (!this.#active()) return;\n this.#present.delete(participantId);\n this.#limitReported.delete(participantId);\n this.#dropEarly(participantId);\n if (this.#heldOffer?.from === participantId) this.#dropHeldOffer();\n if (participantId === this.#activeId) {\n this.#closeLink();\n this.#activeId = null;\n }\n this.#setStatus(participantId, null);\n this.#evaluate();\n }\n\n peerReconnected(participantId: string): void {\n if (!this.#active() || participantId !== this.#activeId) return;\n this.#dropEarly(participantId);\n this.#closeLink();\n this.#setStatus(participantId, null);\n this.#startPairing();\n }\n\n onPeerFrame(message: SignalingMessage): void {\n if (!this.#active()) return;\n const from = message.participantId;\n if (!this.#present.has(from)) {\n this.#holdEarly(message);\n return;\n }\n if (from !== this.#activeId) {\n this.#log.debug(\"frame ignored\", { messageType: message.type, reason: \"not_paired\" });\n return;\n }\n switch (message.type) {\n case \"webrtc.offer\":\n this.#handleOffer(from, message.payload.sdp);\n return;\n case \"webrtc.answer\":\n if (this.#link) this.#link.acceptAnswer(message.payload.sdp);\n else this.#log.debug(\"frame ignored\", { messageType: message.type, reason: \"no_connection\" });\n return;\n case \"webrtc.ice_candidate\":\n if (this.#link) this.#link.addRemoteCandidate(message.payload.candidate);\n else this.#addEarlyCandidate(from, message.payload.candidate);\n return;\n case \"webrtc.ice_restart\":\n if (this.#link?.role === \"offerer\") this.#link.restart(\"peer_request\");\n else this.#log.debug(\"frame ignored\", { messageType: message.type, reason: this.#link ? \"answerer\" : \"no_connection\" });\n return;\n default:\n return;\n }\n }\n\n iceServersUpdated(iceServers: readonly IceServerConfig[], expiresAt: number): void {\n if (!this.#active()) return;\n this.#iceServers = iceServers;\n const link = this.#link;\n if (!link) return;\n link.updateIceServers(iceServers);\n link.planSwitch(expiresAt);\n }\n\n localMediaChanged(kind: MediaKind, stream: MediaStream | null): void {\n if (this.#closed) return;\n if (kind === \"screen\") {\n const track = stream?.getVideoTracks()[0] ?? null;\n this.#local.screen = track;\n this.#link?.attachLocal(\"screen\", track);\n return;\n }\n const track = (kind === \"camera\" ? stream?.getVideoTracks()[0] : stream?.getAudioTracks()[0]) ?? null;\n // A muted microphone keeps its disabled track on the connection, so it sends silence.\n if (kind === \"microphone\" && track === null) return;\n this.#local[kind] = track;\n this.#link?.attachLocal(kind, track);\n }\n\n supportsScreenShare(): boolean {\n return this.#active();\n }\n\n remoteMedia(participantId: string): RemoteMedia {\n return this.#remote.get(participantId);\n }\n\n connectionStatus(participantId: string): MediaConnectionStatus | null {\n return this.#status.get(participantId) ?? null;\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.#unsubscribeNetwork?.();\n this.#unsubscribeNetwork = undefined;\n this.#episode = null;\n const link = this.#link;\n this.#link = null;\n link?.close();\n for (const participantId of [...this.#early.keys()]) this.#dropEarly(participantId);\n this.#dropHeldOffer();\n this.#present.clear();\n this.#status.clear();\n this.#remote.clear();\n this.#activeId = null;\n }\n\n #active(): boolean {\n return this.#context !== undefined && !this.#inert && !this.#closed;\n }\n\n /** The single-peer rule, applied after every presence change. */\n #evaluate(): void {\n const others = this.#present.size;\n if (this.#activeId !== null && others >= 2 && !this.#keepsPair()) {\n const abandoned = this.#activeId;\n this.#closeLink();\n this.#activeId = null;\n this.#setStatus(abandoned, null);\n this.#log.debug(\"media connection abandoned\", { participantId: abandoned });\n }\n if (this.#activeId === null && others === 1) {\n const [only] = this.#present.keys();\n if (only !== undefined) {\n this.#activeId = only;\n this.#startPairing();\n }\n }\n if (others >= 2) {\n for (const participantId of this.#present.keys()) {\n if (participantId === this.#activeId && this.#keepsPair()) continue;\n if (this.#limitReported.has(participantId)) continue;\n this.#limitReported.add(participantId);\n this.#log.info(\"media not carried\", { participantId, code: \"media_participant_limit\" });\n this.#emit(\"error\", { error: new MeetingError(\"media_participant_limit\") });\n }\n }\n }\n\n /** A pair is kept once its link has connected, until the peer leaves, reconnects, or the link fails. */\n #keepsPair(): boolean {\n return this.#link !== null && this.#link.everConnected && this.#link.status !== \"failed\";\n }\n\n #startPairing(): void {\n const peerId = this.#activeId;\n const self = this.#context?.participantId;\n if (peerId === null || self === undefined) return;\n if (roleFor(self, peerId) !== \"offerer\") return;\n this.#createLink(peerId)?.start();\n }\n\n #handleOffer(from: string, sdp: string): void {\n const self = this.#context?.participantId;\n if (self === undefined) return;\n if (roleFor(self, from) === \"offerer\") {\n this.#log.debug(\"frame ignored\", { messageType: \"webrtc.offer\", reason: \"offerer\" });\n return;\n }\n let link = this.#link;\n if (link?.startsNewSession(sdp)) {\n // The other side replaced its connection; answer on a fresh one and keep the status as an interruption.\n const interrupted = link.everConnected;\n this.#link = null;\n link.close();\n if (interrupted) this.#openEpisode(\"media\").replaced = true;\n link = this.#createLink(from, interrupted ? \"interrupted\" : \"connecting\");\n this.#refreshRemote(from);\n }\n link ??= this.#createLink(from);\n link?.acceptOffer(sdp);\n }\n\n #createLink(peerId: string, initialStatus: MediaConnectionStatus = \"connecting\"): PeerLink | null {\n const context = this.#context;\n if (!context) return null;\n let link: PeerLink;\n try {\n link = new PeerLink({\n env: this.#env,\n log: this.#log,\n selfId: context.participantId,\n peerId,\n iceServers: this.#iceServers,\n timeoutMs: this.#timeoutMs,\n local: this.#local,\n callbacks: {\n sendOffer: (payload) => {\n if (this.#link === link) context.send(\"webrtc.offer\", payload);\n },\n sendAnswer: (payload) => {\n if (this.#link === link) context.send(\"webrtc.answer\", payload);\n },\n sendCandidate: (payload) => {\n if (this.#link === link) context.send(\"webrtc.ice_candidate\", payload);\n },\n statusChanged: (status, reason) => {\n if (this.#link === link) this.#onLinkStatus(link, status, reason);\n },\n connected: (report) => {\n if (this.#link === link) context.send(\"webrtc.connected\", { peerParticipantId: peerId, ...report });\n },\n requestRestart: () =>\n this.#link === link &&\n this.#serverSupportsRestart &&\n context.send(\"webrtc.ice_restart\", { targetParticipantId: peerId }),\n recoveryStarted: (trigger) => {\n if (this.#link === link) this.#recoveryStarted(trigger);\n },\n switchResult: (outcome) => {\n if (this.#link === link && this.#serverSupportsRestart) {\n context.send(\"webrtc.ice_restart_result\", { peerParticipantId: peerId, outcome });\n }\n },\n qualitySample: (sample) => {\n if (this.#link !== link) return;\n try {\n context.send(\"webrtc.quality_sample\", { peerParticipantId: peerId, ...sample });\n } catch {\n // Send failure must not tear down media.\n }\n },\n },\n });\n } catch {\n this.#log.debug(\"negotiation step failed\", { participantId: peerId, reason: \"create\" });\n this.#reportFailure(peerId, \"negotiation_failed\");\n return null;\n }\n this.#link = link;\n this.#setStatus(peerId, initialStatus);\n const early = this.#early.get(peerId);\n if (early) {\n this.#dropEarly(peerId);\n for (const candidate of early.candidates) link.addRemoteCandidate(candidate);\n }\n return link;\n }\n\n #onLinkStatus(link: PeerLink, status: LinkStatus, reason: LinkFailure | undefined): void {\n const peerParticipantId = link.peerId;\n switch (status) {\n case \"connected\":\n this.#recovered(peerParticipantId);\n this.#setStatus(peerParticipantId, \"connected\");\n this.#refreshRemote(peerParticipantId);\n return;\n case \"interrupted\":\n this.#openEpisode(\"media\");\n this.#context?.send(\"webrtc.disconnected\", { peerParticipantId });\n this.#setStatus(peerParticipantId, \"interrupted\");\n return;\n case \"failed\":\n this.#link = null;\n link.close();\n if (this.#replaceFailedLink(link)) return;\n this.#giveUp(peerParticipantId);\n this.#reportFailure(peerParticipantId, reason ?? \"connection_failed\");\n return;\n default:\n return;\n }\n }\n\n #openEpisode(trigger: RecoveryTrigger): RecoveryEpisode {\n this.#episode ??= { startedAt: this.#env.now(), attempts: 0, trigger, replaced: false };\n return this.#episode;\n }\n\n #recoveryStarted(trigger: RecoveryTrigger): void {\n const episode = this.#openEpisode(trigger);\n episode.attempts += 1;\n episode.trigger = trigger;\n this.#log.info(\"reconnect attempt started\", reconnectLogFields({ attempt: episode.attempts, trigger }));\n }\n\n /** The connection is carrying media again after a drop. */\n #recovered(peerParticipantId: string): void {\n const episode = this.#episode;\n if (!episode) return;\n this.#episode = null;\n this.#log.info(\n \"reconnect succeeded\",\n reconnectLogFields({\n attempt: Math.max(1, episode.attempts),\n trigger: episode.trigger,\n elapsedMs: Math.max(0, this.#env.now() - episode.startedAt),\n participantId: peerParticipantId,\n }),\n );\n }\n\n /** Recovery is over without media coming back. */\n #giveUp(peerParticipantId: string): void {\n const episode = this.#episode;\n if (!episode) return;\n this.#episode = null;\n const elapsedMs = Math.max(0, this.#env.now() - episode.startedAt);\n this.#log.warn(\n \"reconnect failed\",\n reconnectLogFields({\n attempt: Math.max(1, episode.attempts),\n elapsedMs,\n reason: elapsedMs >= this.#recoveryWindowMs ? \"recovery_window_exhausted\" : \"hard_failure\",\n participantId: peerParticipantId,\n }),\n );\n }\n\n /**\n * A connection that carried media and then failed is replaced once, within the recovery window, before media is\n * given up. The meeting connection is untouched. False when no replacement is allowed or it could not be made.\n */\n #replaceFailedLink(failed: PeerLink): boolean {\n const peerId = failed.peerId;\n if (!failed.everConnected || peerId !== this.#activeId || !this.#present.has(peerId)) return false;\n const episode = this.#openEpisode(\"media\");\n if (episode.replaced || this.#env.now() - episode.startedAt >= this.#recoveryWindowMs) return false;\n episode.replaced = true;\n episode.attempts += 1;\n this.#log.info(\"reconnect attempt started\", reconnectLogFields({ attempt: episode.attempts, trigger: episode.trigger }));\n const next = this.#createLink(peerId, \"interrupted\");\n if (!next) {\n // Creating the connection failed and was reported as a failure already.\n this.#episode = null;\n return true;\n }\n this.#refreshRemote(peerId);\n next.start();\n return true;\n }\n\n /** Reports a failed link. The pair stays as it is; nothing more is tried until the peer reconnects or rejoins. */\n #reportFailure(peerParticipantId: string, reason: LinkFailure): void {\n this.#log.warn(\"media connection failed\", { participantId: peerParticipantId, reason });\n this.#context?.send(\"webrtc.failed\", { peerParticipantId, reason });\n this.#setStatus(peerParticipantId, \"failed\");\n this.#refreshRemote(peerParticipantId);\n this.#emit(\"error\", { error: new MeetingError(\"media_connection_failed\") });\n }\n\n /** Closes the current link without reporting a failure, and reports its remote media as gone. */\n #closeLink(): void {\n const link = this.#link;\n if (!link) return;\n this.#link = null;\n this.#episode = null;\n link.close();\n this.#refreshRemote(link.peerId);\n }\n\n #refreshRemote(participantId: string): void {\n const link = this.#link;\n const active = participantId === this.#activeId && link !== null && link.peerId === participantId;\n const next = computeRemoteMedia({\n active,\n usable: active && link.usable,\n announced: this.#present.get(participantId) ?? NOTHING_ANNOUNCED,\n streams: {\n camera: link?.remoteStream(\"camera\") ?? null,\n microphone: link?.remoteStream(\"microphone\") ?? null,\n screen: link?.remoteStream(\"screen\") ?? null,\n },\n });\n this.#remote.update(participantId, next, (event) => this.#emit(\"remote_media_changed\", event));\n }\n\n #setStatus(participantId: string, status: MediaConnectionStatus | null): void {\n const previous = this.#status.get(participantId) ?? null;\n if (previous === status) return;\n if (status === null) this.#status.delete(participantId);\n else this.#status.set(participantId, status);\n this.#emit(\"media_connection_changed\", { participantId, status });\n }\n\n #holdEarly(message: SignalingMessage): void {\n switch (message.type) {\n case \"webrtc.offer\": {\n this.#dropHeldOffer();\n const from = message.participantId;\n this.#heldOffer = {\n from,\n sdp: message.payload.sdp,\n timer: this.#env.setTimeout(() => {\n if (this.#heldOffer?.from === from) this.#heldOffer = undefined;\n }, EARLY_FRAME_HOLD_MS),\n };\n return;\n }\n case \"webrtc.ice_candidate\":\n this.#addEarlyCandidate(message.participantId, message.payload.candidate);\n return;\n default:\n this.#log.debug(\"frame ignored\", { messageType: message.type, reason: \"unknown_participant\" });\n }\n }\n\n #addEarlyCandidate(participantId: string, candidate: IceCandidate): void {\n let early = this.#early.get(participantId);\n if (!early) {\n early = {\n candidates: [],\n timer: this.#env.setTimeout(() => this.#early.delete(participantId), EARLY_FRAME_HOLD_MS),\n };\n this.#early.set(participantId, early);\n }\n if (early.candidates.length >= MAX_EARLY_CANDIDATES) {\n this.#log.debug(\"late candidate dropped\", { participantId, reason: \"queue_full\" });\n return;\n }\n early.candidates.push(candidate);\n }\n\n #dropEarly(participantId: string): void {\n const early = this.#early.get(participantId);\n if (!early) return;\n this.#env.clearTimeout(early.timer);\n this.#early.delete(participantId);\n }\n\n #dropHeldOffer(): void {\n if (this.#heldOffer) this.#env.clearTimeout(this.#heldOffer.timer);\n this.#heldOffer = undefined;\n }\n\n #emit<K extends DeliveryEventType>(type: K, event: MeetingEventMap[K]): void {\n if (!this.#closed) this.#context?.emit(type, event);\n }\n}\n", "import { currentEnvironment } from \"./internal/env.js\";\nimport { MeetingEngineImpl } from \"./internal/engine.js\";\nimport { SignalingOnlyMediaDelivery } from \"./internal/mediaDelivery.js\";\nimport { PeerMediaDelivery } from \"./internal/peerMediaDelivery.js\";\nimport { clampReconnectWindowMs } from \"./internal/reconnect/recoveryWindow.js\";\nimport { decodeAddressing } from \"./internal/token.js\";\nimport { MeetingError } from \"./public/errors.js\";\nimport type { MeetingEngine, MeetingEngineOptions } from \"./public/types.js\";\n\nconst DEFAULT_JOIN_TIMEOUT_MS = 15_000;\nconst MIN_JOIN_TIMEOUT_MS = 1_000;\nconst MAX_JOIN_TIMEOUT_MS = 60_000;\nconst LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\"]);\n\nfunction normalizeRealtimeUrl(value: unknown): string {\n const invalid = new TypeError(\n \"realtimeUrl must be wss://host[:port] (ws:// only for localhost) with no path, query, or fragment.\",\n );\n if (typeof value !== \"string\" || value.includes(\"?\") || value.includes(\"#\")) throw invalid;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw invalid;\n }\n const secure = url.protocol === \"wss:\";\n const loopback = url.protocol === \"ws:\" && LOOPBACK_HOSTS.has(url.hostname);\n if (!secure && !loopback) throw invalid;\n if (url.username !== \"\" || url.password !== \"\" || (url.pathname !== \"\" && url.pathname !== \"/\")) throw invalid;\n return `${url.protocol}//${url.host}`;\n}\n\nfunction normalizeJoinTimeout(value: unknown): number {\n if (value === undefined) return DEFAULT_JOIN_TIMEOUT_MS;\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < MIN_JOIN_TIMEOUT_MS || value > MAX_JOIN_TIMEOUT_MS) {\n throw new TypeError(`joinTimeoutMs must be an integer from ${MIN_JOIN_TIMEOUT_MS} to ${MAX_JOIN_TIMEOUT_MS}.`);\n }\n return value;\n}\n\n/**\n * Creates a meeting engine for one participant. Pass the join token your backend issued and the realtime\n * service URL, then call `join()`.\n *\n * @throws TypeError when `realtimeUrl` or `joinTimeoutMs` is malformed.\n * @throws MeetingError `join_token_invalid` when the token is not a valid join token, or\n * `unsupported_environment` when the browser has no WebSocket support.\n */\nexport function createMeetingEngine(options: MeetingEngineOptions): MeetingEngine {\n if (typeof options !== \"object\" || options === null) throw new TypeError(\"createMeetingEngine requires options.\");\n const realtimeUrl = normalizeRealtimeUrl(options.realtimeUrl);\n const joinTimeoutMs = normalizeJoinTimeout(options.joinTimeoutMs);\n const token: unknown = options.token;\n if (typeof token !== \"string\" || token.length === 0) throw new MeetingError(\"join_token_invalid\");\n const env = currentEnvironment();\n if (!env.WebSocket) throw new MeetingError(\"unsupported_environment\");\n const addressing = decodeAddressing(token);\n const getJoinToken = options.getJoinToken;\n if (getJoinToken !== undefined && typeof getJoinToken !== \"function\") {\n throw new TypeError(\"getJoinToken must be a function when provided.\");\n }\n const reconnectWindowMs = clampReconnectWindowMs(options.reconnectWindowMs);\n return new MeetingEngineImpl({\n token,\n realtimeUrl,\n joinTimeoutMs,\n logger: options.logger,\n addressing,\n env,\n getJoinToken: typeof getJoinToken === \"function\" ? getJoinToken : undefined,\n reconnectWindowMs,\n createMediaDelivery: (mediaMode, deps) =>\n mediaMode === \"p2p\"\n ? new PeerMediaDelivery({ ...deps, recoveryWindowMs: reconnectWindowMs })\n : new SignalingOnlyMediaDelivery(),\n });\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["role", "role", "reject", "iceServers", "iceServers", "sdp", "KINDS", "iceServers", "track", "sdp"]
7
+ }