diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..de8fcaf3d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# uniffi-generated MatrixRTC SDK bindings, vendored by scripts/sync-matrix-rtc-sdk.sh +src/matrix-rtc-sdk/generated/** linguist-generated=true +src/matrix-rtc-sdk/generated/**/*.wasm binary diff --git a/.oxfmtrc.json b/.oxfmtrc.json index 20bac1bf8..44be81943 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -2,5 +2,10 @@ "$schema": "./node_modules/oxfmt/configuration_schema.json", "printWidth": 80, "sortPackageJson": false, - "ignorePatterns": ["pnpm-lock.yaml", "node_modules", "dist"] + "ignorePatterns": [ + "pnpm-lock.yaml", + "node_modules", + "dist", + "src/matrix-rtc-sdk/generated" + ] } diff --git a/.oxlintrc.json b/.oxlintrc.json index c89ab8a28..cf1cef833 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -28,6 +28,9 @@ "env": { "builtin": true }, + // uniffi-generated bindings and wasm-bindgen glue, vendored by + // scripts/sync-matrix-rtc-sdk.sh; not ours to lint + "ignorePatterns": ["src/matrix-rtc-sdk/generated/**"], "rules": { "element-call/copyright-header": [ "error", diff --git a/element-call-oxidation-plan.md b/element-call-oxidation-plan.md new file mode 100644 index 000000000..e60ab4c74 --- /dev/null +++ b/element-call-oxidation-plan.md @@ -0,0 +1,730 @@ +# Element Call oxidation plan + +Move Element Call's MatrixRTC participation logic off `matrix-js-sdk`'s +`MatrixRTCSession` and onto the Rust `matrix-rtc` crate +(`~/Projects/matrix-rust-rtc/MatrixSdkArchitectureDraft`), consumed through its +uniffi wasm bindings. The Element Call **component** then depends on one +host-supplied object, a `MatrixDriver`, and never on a `MatrixClient`. The +standalone app and the widget become hosts like any other: they build a +`MatrixDriver` from their `matrix-js-sdk` client and hand it to the component. + +Revision 2 — incorporates the independent review (§10 lists what changed and +the assumptions taken where only the user can decide). + +Status legend: ☐ todo · ◐ in progress · ☑ done. + +**Where things stand (2026-09-14):** S0a, S0b, S1a, S1b and S2 are +implemented and green (`pnpm lint`, `pnpm format:check`, `pnpm test:unit`: +101 files / 785 tests). Nothing is committed yet, in either repository: the +crate changes (C2–C8; C1 was reverted in favour of slot opening; C11 and C12 done; +C9 and C10 are pending) sit uncommitted in +`~/Projects/matrix-rust-rtc/MatrixSdkArchitectureDraft`, and Element Call's +branch `toger5/oxidation` holds the vendored bindings, the driver layer +(`src/driver/**`), the participation layer (`src/state/rtc/**`) and the +config/lint changes. S3a is the next slice; its design is in §6 and the +files it touches are listed there. + +--- + +## 1. Goals and non-goals + +**Goals** + +1. `ElementCall` (component) takes `driver: MatrixDriver` instead of + `client: MatrixClient`. Nothing rendered under `CallView` imports + `MatrixClient`, `Room`, `RoomMember`, `MatrixEvent` or + `matrix-js-sdk/lib/matrixrtc`. +2. All MatrixRTC participation logic (session projection, own membership + join/leave/keep-alive, transport tokens, media key exchange) comes from the + crate's `FfiParticipationManager`. Element Call keeps only what the crate + deliberately leaves to the host: the LiveKit media plane, tiles, room + metadata, reactions, notifications, UI. +3. The standalone SPA, widget mode and `sdk/main.ts` construct a + `JsSdkMatrixDriver` (a port of the draft's `web-test-app/src/jsSdkDriver.ts` + that also works on js-sdk's `RoomWidgetClient`, extended with what Element + Call needs beyond RTC) and stop using `client.matrixRTC`. +4. Every existing gate stays green: `pnpm lint` (tsc, oxlint, knip, component + externals), `pnpm format:check`, `pnpm test` (unit + storybook), + `pnpm i18n:check`, all four builds, Playwright (standalone, widget, + component). + +**Non-goals** + +- Replacing `matrix-js-sdk` in the standalone shell (login, registration, room + creation, home page, crypto bootstrap). The shell keeps its client and wraps + it. `src/home/useGroupCallRooms.ts` stays on `client.matrixRTC` for now. +- Removing `matrix-js-sdk/lib/logger`. It is isolated behind + `src/utils/logger.ts` (S6) so a later swap is one line. +- Writing a matrix-rust-sdk-backed driver (Element X). The interface is shaped + so one can be written; none is written here. +- Publishing the crate as an npm package. Until it exists, the generated + bindings are vendored (§5.1). +- Turning on MSC4153 (cross-signed sender) enforcement. Parity first (§5.8). + +--- + +## 2. Where the code is today (inventory) + +Entry: `component/index.tsx` → `src/room/CallView.tsx` → `LobbyView` | +`ActiveCall` (`src/room/InCallView.tsx`) | `CallEndedView`. `ActiveCall` +builds the view model with +`createCallViewModel$(scope, rtcSession, matrixRoom, mediaDevices, muteStates, options, raisedHands$, reactions$, trackProcessorState$)`. + +| Concern | Files | js-sdk surface used | +| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Session memberships | `src/state/SessionBehaviors.ts`, `src/useMatrixRTCSessionMemberships.ts` | `rtcSession.memberships`, `MembershipsChanged`, `membership.getTransport`, `isKeyRotationSuppressed` | +| Own membership | `localMember/LocalMember.ts` (`enterRTCSession`), `localMember/HomeserverConnected.ts` | `joinRTCSession`, `leaveRoomSession(1000)`, `updateCallIntent`, `MembershipManagerEvent.*`, `ClientEvent.Sync`; delegation probe at `LocalMember.ts:298-313`, delegation through the JWT service at `:731-756` | +| SFU / JWT | `src/livekit/openIDSFU.ts`, `localMember/LocalTransport.ts`, `localMember/RtcTransportAutoDiscovery.ts`, `remoteMembers/Connection.ts`, `ConnectionFactory.ts`, `ConnectionManager.ts` | `getOpenIdToken`, `_unstable_getRTCTransports`, `POST /get_token` (+ `delay_id`, `delay_timeout`, `delay_cs_api_url`), legacy `/sfu/get` | +| Remote member ↔ LiveKit identity | `remoteMembers/MatrixLivekitMembers.ts` | `rtcBackendIdentity`, `userId`, `deviceId`, `memberId` | +| E2EE media keys | `src/e2ee/matrixKeyProvider.ts`, `src/e2ee/sharedKeyManagement.ts` | `EncryptionKeyChanged`, `reemitEncryptionKeys`, `room.hasEncryptionStateEvent` | +| Room metadata | `remoteMembers/MatrixMemberMetadata.ts`, `src/utils/displayname.ts`, `src/room/useRoomName.ts`, `useRoomState.ts`, `useRoomAvatar.ts`, `useJoinRule.ts`, `InviteModal.tsx`, `CallView.tsx` | `getMembersWithMembership`, `RoomStateEvent.Members`, `room.name`, `getMxcAvatarUrl`, `getJoinRule`, `getCanonicalAlias` | +| Own profile / avatars | `src/profile/useProfile.ts`, `src/Avatar.tsx` | `getUser`, `UserEvent.*`, `setDisplayName`, `setAvatarUrl`, `uploadContent`, `mxcUrlToHttp`, `getAccessToken` | +| Reactions / hand raise | `src/reactions/ReactionsReader.ts`, `useReactionsSender.tsx`, `src/reactions/index.ts` | `RoomEvent.Timeline/Redaction/LocalEchoUpdated`, `MatrixEventEvent.Decrypted`, `relations.getChildEventsForEvent`, `sendEvent`, `redactEvent`, membership `eventId`, `RelationType` | +| Call notifications | `CallViewModel/CallNotificationLifecycle.ts` | `DidSendCallNotification`, `RoomEvent.Timeline` + `EventType.RTCDecline` | +| Rageshake / dev settings | `src/settings/submit-rageshake.ts`, `rageshake.ts`, `FeedbackSettingsTab.tsx`, `DeveloperSettingsTab.tsx` | `getCrypto`, `sendEvent(org.matrix.rageshake_request)`, `ClientEvent.Event`, `secureRandomString`, `doesServerSupportUnstableFeature`, `getSFUConfigWithOpenID` | +| Analytics | `src/analytics/PosthogEvents.ts`, `PosthogAnalytics.ts` | `rtcSession.statistics`, account data | +| Types only | `src/UrlParams.ts`, `src/state/MediaDevices.ts`, `AndroidControlledAudioOutput.ts`, `IOSControlledAudioOutput.ts`, `initialMuteState.ts`, `state/media/RingingMediaViewModel.ts` (`RTCCallIntent`), `src/useEvents.ts` (`TypedEventEmitter` types) | replaced by a local `CallIntent` type / kept as generic emitter typing | +| Runtime misc | `src/useLocalStorage.ts` (`TypedEventEmitter`), `src/room/GroupCallErrorBoundary.tsx` (`MatrixError`), `src/room/KnockLobbyView.tsx` (shell) | see S6 | +| Context | `src/ClientContext.tsx` | `useClient`/`useClientState` used by `Avatar`, `sharedKeyManagement`, `useReactionsSender`, `submit-rageshake`, `DisconnectedBanner` | + +Hosts: `component/index.tsx:291`, `src/room/useLoadGroupCall.ts:335`, +`sdk/main.ts:128` (own `MatrixRTCSessionManager`; waits on +`MatrixRTCSessionEvent.JoinStateChanged` at `:292`). Test kit: +`src/utils/test.ts` (`MockRTCSession`, `mockRtcMembership`, `mockMatrixRoom`), +`src/utils/test-viewmodel.ts`, `CallViewModelTestUtils.ts`. Baseline on +`main` (fe911628): tsc green, 97 unit files / 757 tests green. + +Component build: `vite-component.config.ts` (single string `fileName`, +externals list with 18 `matrix-js-sdk/lib/*` subpaths), `pnpm lint:externals`, +`component/package.json` (`matrix-js-sdk: "*"` peer, `exports` without a +wildcard). + +--- + +## 3. What the crate gives us, what it does not, and what must change in it + +Verified against `src/uniffi_api/mod.rs`, `src/participation/mod.rs`, +`src/session/state.rs`, `src/own_membership/machine.rs`, +`src/encryption/matrix_encryption_event.rs` and the generated +`web-test-app/src/generated/matrix_rtc.ts` (acceptance suites: 32 pass). + +**Provided** (`FfiParticipationManager`, one per `(room, slot)`, any number +share one `FfiMatrixDriver`): + +- `join(FfiTransportIntent, FfiJoinParams)` / `leave(code?, reason?)`; a + `Publish` intent with a bare LiveKit transport triggers discovery through + `driver.getRtcTransports()` (`connections/mod.rs:470-499`); a driver _error_ + there is `NoTransport`, not a fallback. +- `memberships()` + listener: `FfiMembership { member { memberId, userId, +deviceId, displayName?, avatarUrl?, intent?, applicationType?, +publishedTransports, canSubscribe }, state: Joined | LeftWithKeys, +connections: serviceUrl[] (the FFI doc comment saying ws urls is wrong), +transportIdentity?, mediaKey? }`. `transportIdentity` is today's + `rtcBackendIdentity`. `LeftWithKeys` entries have empty `connections`. +- `connections()` + listener: `{ connection { serviceUrl, wsUrl, jwtToken, +expiresAtTs }, members }[]`; tokens re-minted a minute before `exp`. +- `keyMap()` + `setKeyMapListener(map, change)`: `FfiMediaKey { memberId, key: +ArrayBuffer, index, creationTsMs: bigint }`, inbound keys **and our own** + (`encryption/inbound.rs:251-257`). +- `status()` + listener: `Disconnected{cause} | Joining | Connected{keepAlive, +membership, roster, encryption, impairments} | Leaving`. +- `session()`, `ownMemberId()` (available as soon as `join()` starts), + `ownMembership()`, `connectionProblems()`, `debugSnapshot()`. +- Member display names and avatars, from the room's `m.room.member` state + (C8): the session keeps them current, so a rename is a memberships change. +- Slots are required: once the seed has read slot state, a slot with no event + is closed, so a room without an `m.rtc.slot` has no call, in every dialect. + `openSlot(application, encrypted)` / `closeSlot()` send the state event; + the slot id `m.call#ROOM` matches js-sdk's default. +- `FfiElementCallCompat.{Off, StickyEvents, StateEvents}` today; `StickyEvents` + is removed by C9, leaving `Off` (spec MSC4143) and `StateEvents` (MSC3401). +- All `u64` fields are `bigint` in TypeScript; `Vec` is `ArrayBuffer`. +- Listener callbacks arrive one timer tick after the emitting call (pumps + sleep through `setTimeout`, `executor.rs:86`); getters are fresh. + +**Not provided — Element Call keeps it, through the driver** (§4.1 slices): + +| Need | Why not in the crate | Where it goes | +| ------------------------------------------------------------------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| Room name, alias, avatar, join rule, encryption flag | room metadata | `RoomDriver.getRoomInfo` | +| Profiles of room members who are not in the call (ringing, name-tag threshold) | not RTC | `RoomDriver.subscribeRoomMembers` | +| Reactions, hand raise, `org.matrix.rageshake_request` | application events | `TimelineDriver` | +| MSC4075 notification + decline | out of scope (only `wire_event_type` knows the type) | `TimelineDriver.sendRoomEvent`, decided in `CallNotificationLifecycle` (§4.3) | +| Own profile read/write | not RTC | `ProfileDriver` | +| `mxc://` thumbnails with auth | not RTC | `MediaDriver.thumbnailUrl` | +| Homeserver sync connectivity | needed by the crate too | `RtcMatrixDriver` (`isHomeserverConnected`, `subscribeConnectivity`, C12); reaches Element Call as `HomeserverUnreachable` in the status | +| Sticky-events support probe | capability probe | `MatrixDriver.getCapabilities()` | + +**Must change in the crate (S0a) — each blocks a later slice:** + +| # | Problem | Change | +| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| C1 | A successful `read_state("m.rtc.slot")` returning `[]` marks slot state supplied and every slot other than the legacy `""` resolves `Closed` (`session/state.rs`); `join()` then fails with `SlotClosed` and every MSC4143 peer is excluded. Element Call never sent `m.rtc.slot`. | **Kept as the crate has it: no slot means no call.** Element Call opens the slot when nobody has (`CallParticipation.join` with a `SlotPolicy`: `openSlot("m.call", encrypted)`, then wait for the echo), which needs the power level to send `org.matrix.msc4143.rtc.slot`; without it the join fails with `NoOpenSlotError`. Existing rooms keep working because the first call in a room opens its slot. A compat-mode relaxation was tried and reverted. | +| C2 | `manage_media_keys`, `require_cross_signed_sender`, `use_key_delay_ms` are not settable over the FFI; defaults are `true`, `true`, 1000 ms. | New record `FfiParticipationConfig { compat, manage_media_keys, require_cross_signed_sender, use_key_delay_ms }` as the constructor argument (replaces the bare `compat`). | +| C3 | `StickyEvents` compat sent keys as `org.matrix.msc4143.rtc.encryption_key` while deployed clients read only `io.element.call.encryption_keys`. | Made moot by C9 and reverted with it: the sticky dialect goes away entirely, so `Off` sends the spec key message and `StateEvents` the legacy one, with no middle case. | +| C4 | `FfiMember` has no membership `event_id`; reactions relate to it (§4.3). | `Member.event_id: Option` threaded through `session/dispatch.rs` → `convert/*` → `state.rs` (currently dropped at `state.rs:402`) → `FfiMember.event_id`. | +| C5 | Delegating the delayed leave (MSC4195) is split between the crate and the host: the draft's driver method makes the _adapter_ perform the whole delegation, its demo adapter calls a homeserver endpoint that 401s on a widget client, and Element Call today does it differently (probe, then the authorisation service's `get_token` with `delay_id`/`delay_timeout`/`delay_cs_api_url`). Nothing of this exists in the real `crates/` yet. | **The crate owns the policy; the driver keeps two primitives.** (a) `delegate_delayed_leave_via_homeserver(room, slot, member, delay_id)`: one authenticated POST to the CS API endpoint (`/_matrix/client/unstable/io.element.msc4195/rtc/livekit/delegate_delayed_leave`, the spelling Element Call probes today; a widget client answers `Unsupported`). (b) `LivekitTokenRequest.delegation: Option<{ delay_id, delay_timeout_ms }>`: the adapter appends `delay_id`, `delay_timeout` and `delay_cs_api_url` (its own homeserver URL) to the `get_token` / `sfu/get` body it already sends. The own-membership machine tries (a) first and, on `Unsupported` or any failure, (b) against the transport we publish on, i.e. Element Call's OpenID → JWT → scheduled-event path; only if both fail does it keep restarting the switch itself. **Arm-after-confirm:** the short delayed leave stays armed through the join; delegation arms a second, ≥ 1 h delayed leave, delegates _that_, and cancels the short one on success (or the long one on failure), so no moment is left without an armed leave and a failed delegation costs nothing. `KeepAlive::Delegated` says which route succeeded. The interim C5 (service URL and delay on the request) and Element Call's driver-side probe and JWT delegation are replaced by this. | +| C6 | The transport identity is a pure function (`connections/mod.rs:116-135`) but not exported; the own identity is needed before our membership echo to set our own media key. | Export `FfiParticipationManager.own_transport_identity(): Option`. | +| C7 | Doc comment on `FfiMembership.connections` says `ws_url`s. | Fix the comment. | +| C8 | Both converters set `display_name` / `avatar_url` to `None`, so every host would re-derive them from room members. | The session records each `m.room.member` profile (whether or not the roster condition is enforced) and stamps it on members at projection time. | +| C9 | `ElementCallCompat::StickyEvents` models the 2025 Element Call sticky dialect (`member: {user_id, device_id, id}`, flat `rtc_transports`, `versions`, legacy key message). No deployment uses it: sticky-event calls have not shipped. | **Remove the mode.** Delete `own_membership/compat_2025.rs`, the 2025 block in `session/convert/msc4143.rs` and its dispatch arm, the `StickyEvents` variants of `ElementCallCompat` / `FfiElementCallCompat`, and their tests and acceptance tests; `outbound_event_type` / `build_content` keep two arms (`Off`, `StateEvents`). Element Call then maps `Matrix_2_0 → Off`: spec MSC4143 sticky events with slots (which Element Call opens, C1) and the spec key message. | +| C10 | `MediaKeyState` has `holds_our_key`, `have_their_key` and `rejection`, so a tile learns about an unsigned sender only when `require_cross_signed_sender` is on (the key is discarded, `rejection: NotCrossSigned`). With the check off the verdict is dropped on accept and the tile cannot show an unverified sender. | Keep the MSC4153 verdict of the accepted key per member and expose it as `FfiMediaKeyState.sender_cross_signed: Option` (`None` when the host could not tell). Element Call runs with the check off (§5.8) and wants to show the state on the tile until it is turned on. | +| C11 | No way to change `application["m.call.intent"]` while joined; Element Call flips it between `audio` and `video` when the camera is toggled (`updateCallIntent`). | `update_application(intent)` on the own-membership manager, facade and FFI: while connected the membership is re-published at once on the refresh path (a failure retries like a refresh); during a join the join event carries it; refused with `NotJoined` otherwise. | +| C12 | Homeserver connectivity lived only in Element Call's driver; the crate could not tell a dead homeserver from a quiet one, and a participation's status said nothing about it. | **Done.** `ConnectivityDriver` (`is_homeserver_connected`, `subscribe_connectivity`) joins the `MatrixDriver` sum; the FFI adds `ConnectivitySink`, the two callback methods and `FfiParticipationManager.is_homeserver_connected()`; the facade pump consumes the stream and reports `Impairment::HomeserverUnreachable { since_ts }` (Critical, sorted first) in every non-disconnected status until the driver reports the homeserver back. The web-test-app mock and js-sdk driver implement it. A matrix-rust-sdk adapter implements the same two methods later. | + +No crate work is deferred: `update_application` is C11. + +--- + +## 4. Target architecture + +```text +host (SPA · widget · sdk · a third-party page) + ├─ RtcMatrixDriver = the crate's MatrixDriverCallback, verbatim + │ (events, to-device, tokens, sinks, connectivity) + └─ ElementCallMatrixClientDriver = RoomDriver + TimelineDriver + ProfileDriver + + MediaDriver + capabilities + │ + ▼ component/index.tsx + ┌─ MatrixDriverProvider (src/driver/MatrixDriverContext.tsx) ─────────────┐ + │ CallView owns one CallParticipation for lobby → call → ended │ + │ CallParticipation (src/state/rtc/CallParticipation.ts) │ + │ FfiMatrixDriver(driver) → FfiParticipationManager(room, slot, me, cfg)│ + │ memberships$ · connections$ · keyChanges$ · status$ · session$ │ + │ ownMemberId$ · ownMembership$ · ownTransportIdentity$ │ + │ join(intent, params) · leave(reason) │ + │ │ │ + │ ▼ │ + │ createCallViewModel$(scope, callParticipation, roomInfo, mediaDevices, …) │ + │ ConnectionManager ← connections$ (wsUrl + jwt; no OpenID in EC) │ + │ RemoteMembers ← memberships$ (transportIdentity ↔ LK participant)│ + │ MatrixKeyProvider ← keyChanges$ (memberId → transportIdentity) │ + │ LocalMember ← status$, ownMembership; join/leave → participation│ + │ MemberMetadata ← driver.room members │ + │ Notifications ← driver.timeline + memberships$ │ + │ React: Lobby / InCall / Settings / Avatar / Reactions │ + │ read the driver via useMatrixDriver(), never a client │ + └──────────────────────────────────────────────────────────────────────────┘ +``` + +### 4.1 The two drivers (host-facing, framework-neutral) + +Two files, two objects. `src/driver/RtcMatrixDriver.ts` is one line: the +crate's `MatrixDriverCallback`, re-exported. Everything MatrixRTC, including +homeserver connectivity (C12), goes through it and is consumed by the crate. +`src/driver/ElementCallMatrixClientDriver.ts` is what a call needs beyond +MatrixRTC. Plain TypeScript: async methods and +`subscribeX(listener) → unsubscribe` pairs, mirroring the crate's sink style. +No RxJS crosses this boundary; Element Call wraps subscriptions into +`Behavior`s internally (`src/driver/observe.ts`). + +```ts +export interface ElementCallMatrixClientDriver + extends RoomDriver, TimelineDriver, ProfileDriver, MediaDriver { + readonly userId: string; + readonly deviceId: string; + /** The room this driver is bound to (one driver per room, as in the crate). */ + readonly roomId: string; + getCapabilities(): Promise; + /** Free-form diagnostics for rageshakes (crypto version, sync state, …). */ + getDiagnostics?(): Promise>; +} +export interface DriverCapabilities { + stickyEvents: boolean; + /** The host's events carry decryption metadata (false on a widget client). */ + verifiedEventOrigins: boolean; + /** The host can evaluate MSC4153 cross-signing of senders. */ + crossSigningVerdicts: boolean; +} + +// src/driver/RtcMatrixDriver.ts +export type RtcMatrixDriver = MatrixDriverCallback; + +export interface RoomInfo { + name: string; + canonicalAlias: string | null; + avatarUrl: string | null; + joinRule: string | null; + encrypted: boolean; +} +export interface RoomMemberProfile { + userId: string; + displayName: string | null; + avatarUrl: string | null; + membership: "join" | "invite"; +} +export interface RoomDriver { + getRoomInfo(): RoomInfo; + subscribeRoomInfo(listener: (info: RoomInfo) => void): () => void; + getRoomMembers(): RoomMemberProfile[]; + subscribeRoomMembers( + listener: (members: RoomMemberProfile[]) => void, + ): () => void; +} + +export interface TimelineEvent { + eventId: string; + type: string; + sender: string; + content: Record; + originServerTs: number; + redacts?: string; +} +export interface TimelineDriver { + sendRoomEvent( + eventType: string, + content: unknown, + ): Promise<{ eventId: string }>; + redactEvent(eventId: string): Promise; + /** Decrypted live room events (not sticky), incl. redactions; no local echoes. */ + subscribeTimeline(listener: (event: TimelineEvent) => void): () => void; + /** Events already known that relate to `eventId` (hand-raise catch-up). */ + getRelatedEvents( + eventId: string, + relType: string, + eventType: string, + ): TimelineEvent[]; +} + +export interface OwnProfile { + displayName: string | null; + avatarUrl: string | null; +} +export interface ProfileDriver { + getOwnProfile(): OwnProfile; + subscribeOwnProfile(listener: (profile: OwnProfile) => void): () => void; + /** Absent when the host does not allow profile changes. */ + setDisplayName?(name: string): Promise; + setAvatar?(file: Blob): Promise; +} + +export interface MediaDriver { + /** An ``-usable URL for an mxc thumbnail (may be a blob: URL), or null. */ + thumbnailUrl( + mxcUrl: string, + width: number, + height: number, + resizeMethod: "crop" | "scale", + ): Promise; +} +``` + +The client driver is a union of capability slices, the way the crate splits +its own driver; the RTC driver is the crate's contract untouched, so a host +with a crate-side adapter (matrix-rust-sdk) implements nothing extra for +MatrixRTC. + +### 4.2 `CallParticipation` (Element Call's RxJS view of the manager) + +Naming: _participation_ is the crate's FFI concept (`FfiParticipationManager`, +`FfiParticipationConfig`); `CallParticipation` is Element Call's RxJS wrapper +over it. + +`src/state/rtc/CallParticipation.ts`, a class taking the scope in its constructor: + +```ts +new CallParticipation(scope, driver, { + slotId: "m.call#ROOM", compat, manageMediaKeys, requireCrossSignedSender, + useKeyDelayMs, transportFallbackUrl?, logger }) + memberships$: Behavior> // Joined only; LeftWithKeys filtered (v1) + connections$: Behavior + keyChanges$: Observable // one changed key per emission + keyMap$: Behavior + status$: Behavior + session$: Behavior + ownMemberId$: Behavior + ownTransportIdentity$: Behavior + ownMembership$: Behavior + join(intent: FfiTransportIntent, params: FfiJoinParams): Promise + leave(code?: string, reason?: string): Promise +``` + +- Wraps `new FfiMatrixDriver(driver)` and `new FfiParticipationManager(...)`; + `uniffiDestroy()` on scope end (leave first unless `Disconnected`). +- `transportFallbackUrl` decorates `getRtcTransports`: when the host's call + **throws or returns no LiveKit transport**, answer with + `Config.get().livekit.livekit_service_url` (today's precedence, + `RtcTransportAutoDiscovery.ts:72-94`). +- Lives at `CallView` level (the lobby reads memberships for the participant + count, auto-mute threshold and notification decision, `CallView.tsx:164-413`; + `useReactionsSender` needs the own membership). `join()` after `leave()` on + one manager is supported and mints a fresh member id. +- Behaviors are seeded from the getters and updated by the listeners. + +### 4.3 `createCallViewModel$` after the change + +```ts +createCallViewModel$( + scope, + participation, + roomInfo, + mediaDevices, + muteStates, + options, + handsRaised$, + reactions$, + trackProcessorState$, +); +// roomInfo: { roomId, userId, deviceId, members$: Behavior, +// homeserverConnected$: Behavior, timeline: TimelineDriver } +``` + +| Today | After | +| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `createMemberships$(scope, rtcSession)` | `callParticipation.memberships$` | +| `membershipsAndTransports$` (`getTransport(oldest)`) | `membership.connections[]` (service urls) | +| `createLocalTransport$` + `RtcTransportAutoDiscovery` + `openIDSFU` | deleted; `join(Publish(custom url or bare))`; own transport = `ownMembership.member.publishedTransports[0]` | +| `Connection.start()` fetching a JWT | `Connection` takes `{ wsUrl, jwt, expiresAtTs }` from `connections$`, keyed by `serviceUrl`, holds `token$`; a refreshed token is used on the next full (re)connect; `expiresAtTs` logged on connect; `livekitAlias` decoded from the JWT stays | +| `createRemoteMatrixLivekitMembers$` on `rtcBackendIdentity` | matches `membership.transportIdentity`; key = `member.memberId` | +| `MatrixKeyProvider.setRTCSession` | `MatrixKeyProvider.attach(participation)`: `keyChanges$` × `memberships$` × `ownTransportIdentity$` → `onSetEncryptionKey(material, identity, index)`; keys whose member has no identity yet are held per member id and replayed | +| `enterRTCSession` (`joinRTCSession(...)`) | `callParticipation.join(intent, joinParamsFromConfig(...), { encrypted: roomInfo.encrypted, canOpen: roomInfo.canOpenSlot })` in the same `scope.reconcile`: opens the room's slot first when none is open (power level permitting, otherwise `NoOpenSlotError`), then joins; cleanup calls `callParticipation.leave()` | +| `createHomeserverConnected$` | `status$` alone: `Impairment::HomeserverUnreachable` (C12) = disconnected; `Connected` with `keepAlive` `Armed`/`Delegated`/`Unavailable` = connected; `RestartFailing`/`Expired` = reconnecting (**behaviour change**: local media pauses in that window, today it does not). Outside a participation, `CallParticipation.homeserverConnected$` from the manager's getter | +| `delayId$` + JWT-service delegation | gone from Element Call. `FfiJoinParams.delegateDelayedLeave` is always `true`; the crate tries the CS API, then the authorisation service's token endpoint (with `delay_id`, `delay_timeout`, `delay_cs_api_url`), then falls back to its own restarts (C5). `config.matrix_rtc_session.delegated_delayed_leave.delay_ms` becomes `FfiJoinParams.delegatedDelayMs` (default 1 h) | +| `createMatrixMemberMetadata$(scope, matrixRoom)` | tiles read `member.displayName` / `avatarUrl` from the crate; disambiguation runs over the call's members; `roomInfo.members$` remains only for the ringing name and the name-tag threshold | +| `createSentCallNotification$` / `createReceivedDecline$` | `CallNotificationLifecycle`: after **our own membership echo** (`ownMembership$` non-null) and when no other member was in the session before our join, send `m.rtc.notification` (wire `org.matrix.msc4075.rtc.notification`) via `driver.sendRoomEvent` with the fields js-sdk sends today (`m.mentions`, `notification_type`, `sender_ts`, `lifetime` 90 s, `m.call.intent`, `m.relates_to: m.reference → own membership event id`, `MatrixRTCSession.ts:725-756`); decline from `driver.subscribeTimeline` on both `org.matrix.msc4310.rtc.decline` and `m.rtc.decline` | +| `updateCallIntent` on camera toggle | `callParticipation.updateApplication(videoEnabled ? "video" : "audio")` (C11); Element X's room-header intent keeps following the camera | +| `createKeyRotationSuppressed$` + `key_rotation_participant_limit` | dropped (the crate has no participant limit; the indicator has no equivalent) | +| `rtcSession.statistics` (PostHog) | counts of `keyChanges$` (sent = own member id, received = others) | +| `MembershipManagerError` → `StickyEventsRequiredError` | `status$` `Disconnected{cause: JoinFailed{error: Driver/Unsupported}}` → `StickyEventsRequiredError`; `NoTransport` → `MatrixRTCTransportMissingError`; `ManagerStopped`/`SlotClosed` → `ConnectionLostError` | +| `impairments` | fed into the inert `src/state/ServiceInterruptionsViewModel.ts` (S6) | + +Join parameters (`joinParamsFromConfig`, from `Config.get().matrix_rtc_session`): +`stickyDurationMs = BigInt(Math.min(membership_event_expiry_ms ?? 4 h, 1 h))` +(js-sdk caps sticky at 1 h, the crate clamps to 1 h; the config default is +undefined today); `keepAliveTimeoutMs = BigInt(delayed_leave.delay_ms)`; +`degradedLifetimeMs = undefined`; `applicationType = "m.call"`; +`intent = options.callIntent`. Config keys that stop having an effect and are +documented as such in `docs/`: `delayed_leave.restart_ms`, +`restart_timeout_ms`, `network_error_retry_ms`, +`key_rotation_participant_limit`, `delegated_delayed_leave.*` (the crate arms +1 h when delegating). `wait_for_key_rotation_ms` maps to +`FfiParticipationConfig.useKeyDelayMs`. + +Compat: `MatrixRTCMode.Compatibility → StateEvents`, `Matrix_2_0 → Off` (spec +MSC4143: sticky member events, slots, the spec key message). Until C9 lands the +code still maps `Matrix_2_0` to the crate's `StickyEvents`; `compatForMode` is +the one place that changes. + +### 4.4 React tree + +- `src/driver/MatrixDriverContext.tsx`: `MatrixDriverProvider` holding both + drivers, `useRtcMatrixDriver()` / `useClientDriver()`; replaces every + `useClient()`/`useClientState()` under `CallView`. `ClientContext` stays for the shell. +- `CallView` props: `{ driver, isPasswordlessUser, confineToRoom, preload, skipLobby }`. + It creates the `CallParticipation` (scope tied to its mount) and hands it to + `LobbyView`, `ActiveCall`, `useReactionsSender`. `MatrixInfo` comes from + `driver.getRoomInfo()` / `driver.getOwnProfile()`. +- `InCallView`'s own id becomes `${driver.userId}:${driver.deviceId}`. +- `Avatar` → `driver.thumbnailUrl` (host bridge `downloadMedia` first). +- `useProfile(client)` → `useOwnProfile()`; `ProfileSettingsTab` hides editing + when `setDisplayName` is absent. +- `ReactionsReader(scope, participation, driver)`, `useReactionsSender` over + `TimelineDriver`. Relation target stays the **current own membership event + id** (`member.eventId`, C4) — protocol status quo; the reader keys raised + hands by `memberId` and re-resolves the event id on re-send instead of + dropping the hand. +- `useRoomEncryptionSystem` reads `getRoomInfo().encrypted`. +- `submit-rageshake`: `useMatrixDriver()` for ids and `getDiagnostics?()`; + rageshake requests via `subscribeTimeline`. +- `DeveloperSettingsTab`: sticky probe → `getCapabilities()`; custom LiveKit + URL validation → `driver.getLivekitToken(...)`. +- `DisconnectedBanner` → `HomeserverUnreachable` in `status$` (C12). +- `window.rtcSession` debug handle → `window.matrixRtc = { participation }`. + +### 4.5 Hosts + +- **Component**: `ElementCallProps.rtcDriver: RtcMatrixDriver` and + `clientDriver: ElementCallMatrixClientDriver`; `roomId` stays as an + **optional** prop for one release and must equal `clientDriver.roomId` when given + (assertion), then goes. `initializeElementCall(config, { matrixRtcWasm? })` + awaits `initMatrixRtcSdk()`. Externals shrink to `react*`, `livekit-client`, + `matrix-js-sdk/lib/logger`. `JsSdkRtcMatrixDriver` and `JsSdkElementCallMatrixClientDriver` are + exported from a second entry `@element-hq/element-call-component/matrix-js-sdk` (needs + `lib.fileName` as a function and a new `exports` key); only that entry has + `matrix-js-sdk` as a peer. +- **SPA / widget**: `useLoadGroupCall` returns the `Room`; `RoomPage` memoises + the two js-sdk drivers and renders ``. The + widget capability list in `src/widget.ts` grows by: send and receive state + `m.rtc.slot` and `org.matrix.msc4143.rtc.slot` (opening the slot), `m.room.avatar`, + `m.room.canonical_alias`, `m.room.join_rules`; send/receive to-device + `org.matrix.msc4143.rtc.encryption_key` and `m.rtc.encryption_key` + (alongside `io.element.call.encryption_keys`); events `m.rtc.decline`. +- **`sdk/main.ts`**: builds the driver from the widget client, a + `CallParticipation`, and waits on `status$` instead of `JoinStateChanged`. +- **`component/dev` harness**: the two js-sdk drivers per pane. + +### 4.6 The js-sdk drivers — two classes, two clients each + +`src/driver/jsSdk/JsSdkRtcMatrixDriver.ts` (the crate seam, a port of the +draft's `jsSdkDriver.ts`, connectivity from `ClientEvent.Sync`) and +`JsSdkElementCallMatrixClientDriver.ts` (the §4.1 slices), both written so +that they work on a full `MatrixClient` +**and** on js-sdk's `RoomWidgetClient` (`node_modules/matrix-js-sdk/src/embedded.ts`). +Differences from the draft, all required by the widget client: + +- to-device inbound: listen on `ClientEvent.ToDeviceEvent` (the widget client + never emits `ReceivedToDeviceMessage`, `embedded.ts:788-802`); on a full + client use `ReceivedToDeviceMessage` for the `encryptionInfo`. +- to-device outbound: always `encryptAndSendToDevice` (works without a crypto + backend on the widget client, `embedded.ts:598-612`; plain `sendToDevice` + there is **unencrypted**, `:614-619`). +- transports: `client._unstable_getRTCTransports()` (the widget override, + `embedded.ts:641-646`), never a raw `http.authedRequest`; `.well-known` + fallback only on a full client. +- event origins: on a full client from decryption metadata (as the draft); on + a widget client events arrive decrypted without metadata, so the driver + reports `Encrypted{ senderDeviceId: content.member.device_id }` for member + events and `Encrypted{ senderDeviceId: content.device_id }` for key + events, i.e. the _claimed_ trust level js-sdk applies today + (`ToDeviceKeyTransport.ts:133-140`). `getCapabilities().verifiedEventOrigins` + says which. +- cross-signing verdict: `undefined` on a widget client + (`crossSigningVerdicts: false`); Element Call then forces + `requireCrossSignedSender = false`. +- delegation: two primitives and no policy. `delegateDelayedLeaveViaHomeserver` + is one `authedRequest` on a full client and `Unsupported` on a widget + client; `getLivekitToken` appends `delay_id`, `delay_timeout` and + `delay_cs_api_url` (`client.baseUrl`) when the request carries a + delegation. The crate decides when to call which (C5). Until C5 lands the + driver still carries Element Call's probe and JWT delegation; both go then. +- sticky listener attached after `startClient()` resolves (the widget room + only exists then, `embedded.ts:326`). +- `getLivekitToken` reuses today's request shapes (`slot_id: "m.call#ROOM"`, + legacy `/sfu/get`), errors mapped to `RtcError` incl. `M_LIMIT_EXCEEDED → +RateLimited`, 403 → `Rejected`, 404/`M_UNRECOGNIZED` → `Unsupported`. + +--- + +## 5. Decisions and assumptions + +1. **Vendored bindings.** `src/matrix-rtc-sdk/generated/` holds `matrix_rtc.ts`, + `matrix_rtc-ffi.ts`, `wasm-bindgen/index.js`, `index_bg.wasm` and a + hand-written `wasm-bindgen/index.d.ts` (no `allowJs` in `tsconfig.json`), + synced by `scripts/sync-matrix-rtc-sdk.sh` (runs `ubrn build web` in the + draft **without** the `runtime-probe` feature, copies). Committed so CI + works. `@ubjs/core` becomes a dependency. The wasm is ~6.5 MB unoptimised. + **Assumption:** committing the binary is acceptable for the draft phase. +2. **Wasm loading.** Verified: Vite 8 library mode inlines `?url` and + `new URL(…, import.meta.url)` assets as base64 regardless of + `assetsInlineLimit`; `?url&no-inline` emits a file. App builds use `?url`; + the component build uses `?url&no-inline` plus an `exports` entry for + `./dist/assets/*`, and `initializeElementCall(config, { matrixRtcWasm })` + lets a host point elsewhere. vitest reads the file from disk; wasm boot is + **lazy** (only suites that need it call `initMatrixRtcSdk()`), never in + `src/vitest.setup.ts`; Storybook boots it in `.storybook/preview.tsx` + `beforeAll`. Suites using `vi.useFakeTimers` never share a file with + real-wasm tests (pumps sleep on `setTimeout`). +3. **Own identity via the driver.** `userId`/`deviceId` are properties of the + driver, not props. +4. **One driver per room**, as in the crate. +5. **`matrix-js-sdk/lib/logger` stays** behind `src/utils/logger.ts`. +6. **`updateCallIntent` stays**, through the crate's `update_application` (C11). +7. **Reactions relate to the current own membership event id** (status quo); + reader keys by `memberId`. Alternatives (stable join event id, or + `memberId` as relation target) are protocol changes left to the user. +8. **MSC4153 default off** (`requireCrossSignedSender = false`) for every + host, confirmed: js-sdk performs no such check today, and a passwordless + SPA peer would otherwise be inaudible to everyone. The intent is to turn + it on; the default carries a TODO (`src/state/rtc/joinParams.ts`), and C10 + puts the sender's verdict on the tile so the UI can show it meanwhile. +9. **Delegation is the crate's alone.** Element Call always asks for it; the + crate tries the CS API, then the authorisation service's token endpoint, + then its own restarts, and arms the long delegated leave only once + delegation is confirmed (C5). Element Call keeps no probe and no + delegation code. +10. **Widget trust model:** origins synthesised from claimed device ids equal + today's js-sdk trust level; the crate records them as + `DeviceAttribution::Verified` because it cannot tell. Documented in the + driver; a `Claimed` attribution flag on the sink is a follow-up crate ask. +11. **Scratch files** go to `agent-workspace/oxidation/`; this plan lives at + the repo root because it was asked for by name. +12. **Branch** `toger5/oxidation`, one commit per slice. + +--- + +## 6. Work breakdown + +Each slice is independently green (`pnpm lint && pnpm test:unit` at least). + +### S0a — crate changes ☑ (in `MatrixSdkArchitectureDraft`) + +C2–C12 from §3 (C1 reverted), each with a Rust unit test; acceptance tests in +`web-test-app/test/`: a slot-less room refuses joins until `openSlot`, in every dialect; +`FfiParticipationConfig` with `manageMediaKeys: false` exchanges no keys; +no `StickyEvents` variant left anywhere (C9); `member.eventId` present; +delegation tries the homeserver first, then the token endpoint with the delay fields, and keeps the short leave armed until one succeeds; +`ownTransportIdentity()` equals the joined membership's `transportIdentity`. +Gate: `cargo test --features uniffi`, `cargo clippy --all-targets --features uniffi -- -D warnings`, +`npm run ubrn:web && npm test`. + +### S0b — SDK intake ☑ + +- `scripts/sync-matrix-rtc-sdk.sh`, `src/matrix-rtc-sdk/generated/**`, + `src/matrix-rtc-sdk/index.ts` (loader + curated re-exports), + `src/matrix-rtc-sdk/index.test.ts` (boot, one join round-trip against the + TS mock driver). +- `package.json` (`@ubjs/core`), `knip.ts` (`ignore` for `generated/**`), + `.oxlintrc.json` `ignorePatterns`, `.oxfmtrc.json` ignore, + `vite.config.ts` (nothing needed for `?url`; verified `vite-plugin-wasm` + ignores it), `tsconfig.json` untouched thanks to the `.d.ts`. +- Gate: `pnpm lint && pnpm format:check && pnpm test:unit`. + +### S1a — driver interface, mock driver ☑ + +- `src/driver/RtcMatrixDriver.ts`, `src/driver/ElementCallMatrixClientDriver.ts`, + `src/driver/observe.ts`, `MockRtcMatrixDriver.ts` (port of + `web-test-app/src/mockDriver.ts`) and `MockElementCallMatrixClientDriver.ts` + (in-memory room info, members, timeline, profile), with a test each. +- `knip.ts` `ignore` for `src/driver/**` until consumed, with the reason. + +### S1b — `JsSdkMatrixDriver` ☑ + +- `src/driver/jsSdk/JsSdkRtcMatrixDriver.ts` and + `JsSdkElementCallMatrixClientDriver.ts` (§4.6) + tests against **two** + fakes: a `MatrixClient`-shaped one (`mockMatrixRoom`) and a + `RoomWidgetClient`-shaped one (`ToDeviceEvent`, no crypto, + `_unstable_getRTCTransports`, sticky updates after `startClient`). + Asserts request shapes of `_unstable_sendStickyEvent`, + `_unstable_sendStickyDelayedEvent`, `_unstable_updateDelayedEvent`, + `encryptAndSendToDevice`, `/get_token` body with and without the delegation fields, `delegateDelayedLeaveViaHomeserver` on both clients, + sink emission and origin synthesis, room-info/member updates. + +### S2 — `CallParticipation` ☑ + +- `src/state/rtc/CallParticipation.ts`, `joinParams.ts`, `transportIntent.ts`, + `errors.ts` (cause → `ElementCallError`). +- `CallParticipation.test.ts` through the real wasm + `MockMatrixDriver`: + memberships follow a remote join/leave; `LeftWithKeys` filtered; join → + `Connected`; `connections$` carries the token; `keyChanges$` fires for a + peer key; `ownTransportIdentity$` set before the echo; leave → + `Disconnected{LeftByHost}`; join → leave → join; scope end destroys the + manager; fallback transport when the host throws or advertises none. + +### S3 — view model, four slices ☐ + +- **S3a** `Connection`/`ConnectionManager`/`ConnectionFactory` keyed by + `serviceUrl`, fed by `Behavior<{ serviceUrl, wsUrl, jwt, expiresAtTs }[]>`, + with a temporary adapter from today's `SFUConfig` so `openIDSFU` stays + until S3c. Introduces `mockCallParticipation()` and `mockFfiMembership()` in + `src/utils/test.ts`. +- **S3b** `MatrixLivekitMembers` + `MatrixKeyProvider` on `FfiMembership` / + key changes, with an adapter from `CallMembership` for the still-js-sdk + `memberships$`. +- **S3c** `LocalMember` on `callParticipation.join/leave` + status-derived + connectivity; delete `LocalTransport.ts`, `RtcTransportAutoDiscovery.ts`, + `HomeserverConnected.ts`, `openIDSFU.ts`, `enterRTCSession`. +- **S3d** `createCallViewModel$` signature, `CallNotificationLifecycle`, + `MatrixMemberMetadata`, `SessionBehaviors.ts` and + `useMatrixRTCSessionMemberships.ts` deleted, `ReactionsReader` on + `participation` + `TimelineDriver`; test kit swapped + (`MockRTCSession`/`mockRtcMembership` deleted). To stay green before S5, + `CallView` builds a `JsSdkMatrixDriver` from its existing `client` / + `rtcSession.room` props as a temporary shim. + +### S4 — React tree, two slices ☐ + +- **S4a** views/hooks/settings on the driver: `CallView.tsx` (owns + `CallParticipation`), `InCallView.tsx`, `LobbyView.tsx`, `CallEndedView.tsx`, + `VideoPreview.tsx`, `useRoomInfo()` (replaces `useRoomName/Avatar/JoinRule/State`), + `InviteModal.tsx`, `Avatar.tsx`, `useOwnProfile.ts`, `ProfileSettingsTab.tsx`, + `SettingsModal.tsx`, `DeveloperSettingsTab.tsx`, `submit-rageshake.ts`, + `DisconnectedBanner.tsx`, `analytics/PosthogEvents.ts`, `controls.ts`, and a + first `CallView.stories.tsx` (lobby, in call, ended) driven by + `MockMatrixDriver`. +- **S4b** reactions and notifications: `useReactionsSender.tsx`, + `ReactionsReader` keyed by `memberId`, `CallNotificationLifecycle` sending + through the driver; tests incl. a membership re-send mid-call. + +### S5 — hosts ☐ + +- `component/index.tsx`, `component/matrix-js-sdk.ts`, `component/package.json` + (`exports`, peers), `vite-component.config.ts` (two entries, `fileName` + function, externals shrink, `?url&no-inline`), `component/tsconfig.build.json`, + `component/dev/Harness.tsx`, `README.md`. +- `src/room/useLoadGroupCall.ts`, `RoomPage.tsx`, `src/widget.ts` + (capabilities, §4.5), `sdk/main.ts`, `playwright/spa-helpers.ts` + (delegation route helper), `docs/` config notes. +- Gate: all four builds; Playwright standalone + widget + component against + `pnpm backend`; a widget media-key round trip and `reconnect.spec.ts` + re-checked under the new delegation path. + +### S6 — fence and cleanup ☐ + +- `src/utils/logger.ts`; local `CallIntent` type for the six `RTCCallIntent` + users; `useLocalStorage.ts` on a local emitter; oxlint + `no-restricted-imports` scoped to the call tree (allow-list: `src/home`, + `src/auth`, `src/utils/spa.ts`, `src/utils/matrix.ts`, `src/driver/jsSdk/**`, + `src/ClientContext.tsx`, `src/widget.ts`, `src/initializer.tsx`, + `src/IndexedDBWorker.ts`, `src/room/KnockLobbyView.tsx`, `src/settings/rageshake.ts`) + banning `matrix-js-sdk` except `matrix-js-sdk/lib/logger`. +- `ServiceInterruptionsViewModel` fed from `status$.impairments`. +- `docs/agents/architecture.md`, `docs/matrix_rtc_modes.md` updated; + `src/@types/matrix-js-sdk.d.ts` removed if nothing merges into js-sdk types. + +--- + +## 7. Risks and mitigations + +| Risk | Mitigation | +| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | +| A user without the power level to open a slot, in a room that never had a call | `NoOpenSlotError` names the remedy; a room admin opens the slot by starting the first call | +| A client on the removed sticky dialect (none is deployed) | Not supported: `Off` and `StateEvents` are the only dialects (C9) | +| Component bundle size / wasm delivery | `?url&no-inline` + `exports` wildcard + `matrixRtcWasm` override; verified in S0b and S5 | +| Token refresh vs LiveKit reconnect | `Connection.token$`; full reconnect uses the latest token; `expiresAtTs` logged | +| Own identity before the echo | C6 export; JWT `sub` only as a logged cross-check | +| Keys before identity | key provider buffers per member id and replays | +| Widget mode regressions (origins, to-device event, capabilities) | S1b tests against a `RoomWidgetClient` fake; S5 widget e2e adds a media-key round trip | +| Delegation fails after the long leave was armed | Arm-after-confirm (C5): the short leave stays armed until delegation is confirmed | +| Behaviour drift in keep-alive (6 s vs 4 s restarts) and dropped config keys | documented in `docs/` | +| Test time: wasm per file | lazy boot in the suites that need it only | + +--- + +## 8. Verification matrix + +| Check | S0a | S0b | S1 | S2 | S3 | S4 | S5 | S6 | +| ---------------------------------------------------------------------------- | --- | --- | --- | --- | --- | --- | --- | --- | +| `cargo test/clippy`, `web-test-app` suites | ● | | | | | | | | +| `pnpm lint` + `format:check` | | ● | ● | ● | ● | ● | ● | ● | +| `pnpm test:unit` | | ● | ● | ● | ● | ● | ● | ● | +| `pnpm test:storybook` | | | | | | ● | ● | ● | +| four builds | | ● | | | | | ● | ● | +| Playwright standalone + widget + component | | | | | | | ● | ● | +| Manual: two harness panes hear each other, E2EE, hand raise, reaction, leave | | | | | | | ● | | + +--- + +## 9. Open questions for the user (answered by the assumptions in §5 until told otherwise) + +1. Slot semantics, **answered**: no slot means no call; Element Call opens the + slot on the first call in a room, power level permitting. +2. Delegation, **answered**: CS API endpoint first, then Element Call's + OpenID → JWT → scheduled-event path, all inside the crate and invisible to + Element Call (C5). +3. Media-key type under the sticky compat, **answered**: there are no deployed + sticky-event clients, so the compat mode is removed (C9) and the question + with it. +4. MSC4153, **answered**: off everywhere for now, with a TODO to turn it on + and C10 so tiles show unverified senders in the meantime. +5. Reactions relation target: current membership event id (taken), stable join + event id, or `memberId`? +6. Committing the 6.5 MB wasm (taken) vs a build-time fetch. +7. `roomId` prop: optional for one release (taken) vs removed outright. +8. `updateCallIntent`, **answered**: added to the crate as `update_application` + (C11); Element Call keeps the behaviour. + +--- + +## 10. Review log + +Independent review findings incorporated in this revision: slot enforcement +blocker (C1); delegation protocol and 1 h arm (C5, §5.9; later redefined as crate-only with arm-after-confirm); `StickyEvents` key +type interop (C3, then made moot by removing the mode, C9); MSC4153 default (§5.8); widget-client differences for the +js-sdk drivers (§4.6) and missing widget capabilities (§4.5); own identity +export (C6); `CallParticipation` lifetime at `CallView` (§4.2); notification +timing and content (§4.3); reactions/event-id semantics (§5.7); config mapping +defaults and dropped keys (§4.3); `bigint`/`ArrayBuffer` types; inventory +gaps (§2); knip `ignore` vs `ignoreFiles`, oxlint/oxfmt ignores, `.d.ts` for +the glue, lazy wasm boot, Storybook `beforeAll`; slice re-cut (S0a/b, S1a/b, +S3a–d, S4a/b, temporary `CallView` shim); `sdk/main.ts` status wiring and the +Playwright delegation helper. diff --git a/knip.ts b/knip.ts index d9de80c8c..200dbb426 100644 --- a/knip.ts +++ b/knip.ts @@ -26,6 +26,15 @@ export default { // This is a shell built-in. "printf", ], + // uniffi-generated bindings (scripts/sync-matrix-rtc-sdk.sh): every + // export the crate has, most of them unused here by design + ignore: [ + "src/matrix-rtc-sdk/generated/**", + // The host-facing driver seam, landed ahead of its consumers (the + // `CallParticipation` layer and the hosts, see element-call-oxidation-plan.md) + "src/driver/**", + "src/state/rtc/**", + ], ignoreFiles: [ "scripts/.pnpmfile.cjs", // Deliberately added prior to any component or business logic diff --git a/locales/en/app.json b/locales/en/app.json index f3d568bb8..7b1775be5 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -110,6 +110,8 @@ "membership_manager": "Membership Manager Error", "membership_manager_description": "The Membership Manager had to shut down. This is caused by many consecutive failed network requests.", "no_matrix_2_authorization_service": "The authorization service for your media server (SFU) is out of date.", + "no_open_slot": "No call to join here", + "no_open_slot_description": "Nobody has started a call in this room yet, and you do not have permission to start one. Ask a room admin to start the call, or to allow you to start calls.", "open_elsewhere": "Opened in another tab", "open_elsewhere_description": "{{brand}} has been opened in another tab. If that doesn't sound right, try reloading the page.", "peer_connection_timeout": "Connection timeout", diff --git a/package.json b/package.json index 90e5e0394..7bfe8dc1c 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "@types/react-dom": "^19.0.0", "@types/sdp-transform": "^2.4.5", "@typescript-eslint/utils": "^8.61.0", + "@ubjs/core": "0.31.0-5", "@use-gesture/react": "^10.2.11", "@vector-im/compound-design-tokens": "^10.0.0", "@vector-im/compound-web": "^10.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d81f4fa4..e80c0a9ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,6 +127,9 @@ importers: '@typescript-eslint/utils': specifier: ^8.61.0 version: 8.69.0(eslint@8.57.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@ubjs/core': + specifier: 0.31.0-5 + version: 0.31.0-5 '@use-gesture/react': specifier: ^10.2.11 version: 10.3.1(react@19.2.8) @@ -3218,6 +3221,9 @@ packages: resolution: {integrity: sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ubjs/core@0.31.0-5': + resolution: {integrity: sha512-oRBRtyYOhaodiOY3rLMZFgAHGFMmD44XefMpsAx4Ja0/rO+45kVdQB1VZsblQMLYxP965i3IHm56eymgo3I5eQ==} + '@ungap/structured-clone@1.3.3': resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} @@ -8686,6 +8692,8 @@ snapshots: '@typescript-eslint/types': 8.69.0 eslint-visitor-keys: 5.0.1 + '@ubjs/core@0.31.0-5': {} + '@ungap/structured-clone@1.3.3': {} '@use-gesture/core@10.3.1': {} diff --git a/scripts/sync-matrix-rtc-sdk.sh b/scripts/sync-matrix-rtc-sdk.sh new file mode 100755 index 000000000..29847f3cf --- /dev/null +++ b/scripts/sync-matrix-rtc-sdk.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# +# Copyright 2026 Element Creations Ltd. +# +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE in the repository root for full details. +# +# Vendors the matrix-rtc crate's uniffi web bindings into +# src/matrix-rtc-sdk/generated/. Until the crate ships as an npm package this +# is how Element Call picks up a new build of it: build it there, copy the +# generated TypeScript, the wasm-bindgen glue and the wasm here. +# +# scripts/sync-matrix-rtc-sdk.sh [path-to-MatrixSdkArchitectureDraft] [--no-build] +# +# The build uses web-test-app/ubrn.element-call.config.yaml (feature `uniffi` +# only, release profile, its own output directory so the app's test build is +# left alone). Pass --no-build to copy whatever was built last. + +set -euo pipefail + +DRAFT="${1:-../matrix-rust-rtc/MatrixSdkArchitectureDraft}" +BUILD=1 +for arg in "$@"; do + [[ "$arg" == "--no-build" ]] && BUILD=0 +done + +APP="$DRAFT/web-test-app" +GENERATED="$APP/src/generated-element-call" +DEST="$(cd "$(dirname "$0")/.." && pwd)/src/matrix-rtc-sdk/generated" + +if [[ ! -f "$APP/ubrn.element-call.config.yaml" ]]; then + echo "No web-test-app/ubrn.element-call.config.yaml under $DRAFT" >&2 + exit 1 +fi + +if [[ "$BUILD" == 1 ]]; then + (cd "$APP" && npx ubrn build web --config ubrn.element-call.config.yaml --release) +fi + +mkdir -p "$DEST/wasm-bindgen" +cp "$GENERATED/matrix_rtc.ts" "$GENERATED/matrix_rtc-ffi.ts" "$DEST/" +cp "$GENERATED/wasm-bindgen/index.js" "$GENERATED/wasm-bindgen/index_bg.wasm" "$DEST/wasm-bindgen/" + +REV="$(git -C "$DRAFT" rev-parse --short HEAD 2>/dev/null || echo unknown)" +DIRTY="$(git -C "$DRAFT" status --porcelain 2>/dev/null | grep -q . && echo '-dirty' || true)" +cat > "$DEST/VERSION" < void; + +export interface DriverCapabilities { + /** The homeserver accepts sticky events (MSC4354). */ + stickyEvents: boolean; + /** + * The events the RTC driver emits carry real decryption metadata. A + * widget client receives events already decrypted by its host and can + * only report the device the content *claims*. + */ + verifiedEventOrigins: boolean; + /** The RTC driver can say whether a sending device is cross-signed (MSC4153). */ + crossSigningVerdicts: boolean; +} + +export interface RoomInfo { + name: string; + canonicalAlias: string | null; + /** An `mxc://` URL. */ + avatarUrl: string | null; + /** The `m.room.join_rules` value, `null` while unknown. */ + joinRule: string | null; + /** Whether the room has an `m.room.encryption` state event. */ + encrypted: boolean; + /** + * Whether this user may send the MatrixRTC slot state event + * (`org.matrix.msc4143.rtc.slot`). A call needs an open slot; the client + * that starts a call opens one, which takes the power level for it. + */ + canOpenSlot: boolean; +} + +export interface RoomMemberProfile { + userId: string; + displayName: string | null; + /** An `mxc://` URL. */ + avatarUrl: string | null; + membership: "join" | "invite"; +} + +/** + * Room metadata and the profiles of the people in it. + * + * Call members' names and avatars do not come from here: the crate reads + * `m.room.member` itself and puts them on each membership. This roster is + * for the people who are in the room but not (yet) in the call — the person + * being rung, and how many others there are. + */ +export interface RoomDriver { + getRoomInfo(): RoomInfo; + subscribeRoomInfo(listener: (info: RoomInfo) => void): Unsubscribe; + /** Joined and invited members of the room, in or out of the call. */ + getRoomMembers(): RoomMemberProfile[]; + subscribeRoomMembers( + listener: (members: RoomMemberProfile[]) => void, + ): Unsubscribe; +} + +/** A decrypted room event, as far as a call needs to know it. */ +export interface TimelineEvent { + eventId: string; + type: string; + sender: string; + content: Record; + originServerTs: number; + /** Set on an `m.room.redaction`. */ + redacts?: string; +} + +/** Application events in the room: reactions, hand raises, notifications. */ +export interface TimelineDriver { + sendRoomEvent( + eventType: string, + content: Record, + ): Promise<{ eventId: string }>; + redactEvent(eventId: string): Promise; + /** + * Live room events (not sticky ones — those reach the crate through its + * own sink), decrypted, redactions included, local echoes excluded. + */ + subscribeTimeline(listener: (event: TimelineEvent) => void): Unsubscribe; + /** + * Events already known that relate to `eventId` with the given relation + * type and event type — how a late joiner learns of a raised hand. + */ + getRelatedEvents( + eventId: string, + relType: string, + eventType: string, + ): TimelineEvent[]; +} + +export interface OwnProfile { + displayName: string | null; + /** An `mxc://` URL. */ + avatarUrl: string | null; +} + +/** The user's own profile. Editing is optional: a host may not allow it. */ +export interface ProfileDriver { + getOwnProfile(): OwnProfile; + subscribeOwnProfile(listener: (profile: OwnProfile) => void): Unsubscribe; + setDisplayName?(name: string): Promise; + setAvatar?(file: Blob): Promise; +} + +export interface MediaDriver { + /** + * A URL an `` can show for an `mxc://` thumbnail — possibly a `blob:` + * URL the driver fetched with its credentials — or null when the media + * cannot be resolved. + */ + thumbnailUrl( + mxcUrl: string, + width: number, + height: number, + resizeMethod: "crop" | "scale", + ): Promise; +} + +/** + * Everything Element Call asks of a Matrix client beyond MatrixRTC, bound to + * one room. One object, sliced into the capabilities above the way the crate + * slices its own driver, so a piece of Element Call can ask for no more than + * it needs. + */ +export interface ElementCallMatrixClientDriver + extends RoomDriver, TimelineDriver, ProfileDriver, MediaDriver { + /** Who we publish as. */ + readonly userId: string; + readonly deviceId: string; + /** The room this driver is bound to. */ + readonly roomId: string; + getCapabilities(): Promise; + /** Free-form facts for a rageshake: crypto version, sync state, and so on. */ + getDiagnostics?(): Promise>; +} diff --git a/src/driver/MockElementCallMatrixClientDriver.test.ts b/src/driver/MockElementCallMatrixClientDriver.test.ts new file mode 100644 index 000000000..2cf952c00 --- /dev/null +++ b/src/driver/MockElementCallMatrixClientDriver.test.ts @@ -0,0 +1,89 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { describe, expect, it, vi } from "vitest"; + +import { MockElementCallMatrixClientDriver } from "./MockElementCallMatrixClientDriver"; +import { MOCK_ROOM_ID } from "./MockRtcMatrixDriver"; + +describe("MockElementCallMatrixClientDriver", () => { + it("notifies room info, member and profile subscribers until unsubscribed", () => { + const driver = new MockElementCallMatrixClientDriver({ + roomInfo: { name: "Standup" }, + }); + expect(driver.getRoomInfo().name).toBe("Standup"); + const onInfo = vi.fn(); + const off = driver.subscribeRoomInfo(onInfo); + driver.setRoomInfo({ name: "Retro" }); + expect(onInfo).toHaveBeenCalledWith( + expect.objectContaining({ name: "Retro" }), + ); + off(); + driver.setRoomInfo({ name: "Planning" }); + expect(onInfo).toHaveBeenCalledTimes(1); + + const onMembers = vi.fn(); + driver.subscribeRoomMembers(onMembers); + const alice = { + userId: "@alice:example.org", + displayName: "Alice", + avatarUrl: null, + membership: "join" as const, + }; + driver.setRoomMembers([alice]); + expect(onMembers).toHaveBeenCalledWith([alice]); + expect(driver.getRoomMembers()).toEqual([alice]); + + const onProfile = vi.fn(); + driver.subscribeOwnProfile(onProfile); + driver.setOwnProfile({ displayName: "Bob" }); + expect(onProfile).toHaveBeenCalledWith({ + displayName: "Bob", + avatarUrl: null, + }); + }); + + it("records sent room events, echoes them, and resolves relations", async () => { + const driver = new MockElementCallMatrixClientDriver(); + const seen: string[] = []; + driver.subscribeTimeline((e) => seen.push(e.type)); + const { eventId } = await driver.sendRoomEvent("m.reaction", { + "m.relates_to": { + rel_type: "m.annotation", + event_id: "$membership", + key: "🖐️", + }, + }); + expect(driver.calls("sendRoomEvent")[0]).toMatchObject({ + eventType: "m.reaction", + eventId, + }); + expect(seen).toEqual(["m.reaction"]); + expect( + driver.getRelatedEvents("$membership", "m.annotation", "m.reaction"), + ).toHaveLength(1); + await driver.redactEvent(eventId); + expect(seen).toEqual(["m.reaction", "m.room.redaction"]); + expect( + driver.getRelatedEvents("$membership", "m.annotation", "m.reaction"), + ).toHaveLength(0); + }); + + it("answers thumbnails for mxc urls only", async () => { + const driver = new MockElementCallMatrixClientDriver(); + await expect( + driver.thumbnailUrl("mxc://example.org/abc", 96, 96, "crop"), + ).resolves.toContain("abc"); + await expect( + driver.thumbnailUrl("https://not-mxc", 96, 96, "crop"), + ).resolves.toBeNull(); + expect(driver.roomId).toBe(MOCK_ROOM_ID); + await expect(driver.getCapabilities()).resolves.toMatchObject({ + stickyEvents: true, + }); + }); +}); diff --git a/src/driver/MockElementCallMatrixClientDriver.ts b/src/driver/MockElementCallMatrixClientDriver.ts new file mode 100644 index 000000000..e52babd82 --- /dev/null +++ b/src/driver/MockElementCallMatrixClientDriver.ts @@ -0,0 +1,246 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +/** + * An {@link ElementCallMatrixClientDriver} whose room lives in memory, for + * tests and stories: room info, members, a timeline and a profile, with + * setters that notify subscribers, and a record of what was sent. + */ + +import { + type DriverCapabilities, + type ElementCallMatrixClientDriver, + type OwnProfile, + type RoomInfo, + type RoomMemberProfile, + type TimelineEvent, + type Unsubscribe, +} from "./ElementCallMatrixClientDriver"; +import { + MOCK_OWN_DEVICE_ID, + MOCK_OWN_USER_ID, + MOCK_ROOM_ID, +} from "./MockRtcMatrixDriver"; + +export type ClientCall = + | { + kind: "sendRoomEvent"; + eventType: string; + content: Record; + eventId: string; + } + | { kind: "redactEvent"; eventId: string }; + +export interface MockElementCallMatrixClientDriverOptions { + userId?: string; + deviceId?: string; + roomId?: string; + roomInfo?: Partial; + members?: RoomMemberProfile[]; + ownProfile?: Partial; + capabilities?: Partial; +} + +export class MockElementCallMatrixClientDriver implements ElementCallMatrixClientDriver { + public readonly userId: string; + public readonly deviceId: string; + public readonly roomId: string; + public readonly outbound: ClientCall[] = []; + + private capabilities: DriverCapabilities; + private roomInfo: RoomInfo; + private members: RoomMemberProfile[]; + private ownProfile: OwnProfile; + private readonly timeline: TimelineEvent[] = []; + + private readonly roomInfoListeners = new Set<(info: RoomInfo) => void>(); + private readonly memberListeners = new Set< + (members: RoomMemberProfile[]) => void + >(); + private readonly timelineListeners = new Set< + (event: TimelineEvent) => void + >(); + private readonly profileListeners = new Set<(profile: OwnProfile) => void>(); + private nextEventId = 0; + + public constructor(options: MockElementCallMatrixClientDriverOptions = {}) { + this.userId = options.userId ?? MOCK_OWN_USER_ID; + this.deviceId = options.deviceId ?? MOCK_OWN_DEVICE_ID; + this.roomId = options.roomId ?? MOCK_ROOM_ID; + this.capabilities = { + stickyEvents: true, + verifiedEventOrigins: true, + crossSigningVerdicts: true, + ...options.capabilities, + }; + this.roomInfo = { + name: "Test room", + canonicalAlias: null, + avatarUrl: null, + joinRule: "public", + encrypted: false, + canOpenSlot: true, + ...options.roomInfo, + }; + this.members = options.members ?? []; + this.ownProfile = { + displayName: "Me", + avatarUrl: null, + ...options.ownProfile, + }; + } + + public calls( + kind: K, + ): Extract[] { + return this.outbound.filter((c) => c.kind === kind) as Extract< + ClientCall, + { kind: K } + >[]; + } + + // --- room ------------------------------------------------------------------ + + public getRoomInfo(): RoomInfo { + return this.roomInfo; + } + + public subscribeRoomInfo(listener: (info: RoomInfo) => void): Unsubscribe { + return listen(this.roomInfoListeners, listener); + } + + public setRoomInfo(info: Partial): void { + this.roomInfo = { ...this.roomInfo, ...info }; + for (const l of this.roomInfoListeners) l(this.roomInfo); + } + + public getRoomMembers(): RoomMemberProfile[] { + return this.members; + } + + public subscribeRoomMembers( + listener: (members: RoomMemberProfile[]) => void, + ): Unsubscribe { + return listen(this.memberListeners, listener); + } + + public setRoomMembers(members: RoomMemberProfile[]): void { + this.members = members; + for (const l of this.memberListeners) l(members); + } + + // --- timeline -------------------------------------------------------------- + + public async sendRoomEvent( + eventType: string, + content: Record, + ): Promise<{ eventId: string }> { + const eventId = `$echo-${this.nextEventId++}`; + this.outbound.push({ kind: "sendRoomEvent", eventType, content, eventId }); + // Like sync, the room sees our event once the server has it. + this.emitTimelineEvent({ + eventId, + type: eventType, + sender: this.userId, + content, + originServerTs: Date.now(), + }); + return Promise.resolve({ eventId }); + } + + public async redactEvent(eventId: string): Promise { + this.outbound.push({ kind: "redactEvent", eventId }); + this.emitTimelineEvent({ + eventId: `$echo-${this.nextEventId++}`, + type: "m.room.redaction", + sender: this.userId, + content: { redacts: eventId }, + originServerTs: Date.now(), + redacts: eventId, + }); + return Promise.resolve(); + } + + public subscribeTimeline( + listener: (event: TimelineEvent) => void, + ): Unsubscribe { + return listen(this.timelineListeners, listener); + } + + /** A room event arrives from another user (or is echoed back). */ + public emitTimelineEvent(event: TimelineEvent): void { + this.timeline.push(event); + for (const l of this.timelineListeners) l(event); + } + + public getRelatedEvents( + eventId: string, + relType: string, + eventType: string, + ): TimelineEvent[] { + const redacted = new Set( + this.timeline.flatMap((e) => (e.redacts ? [e.redacts] : [])), + ); + return this.timeline.filter((e) => { + if (e.type !== eventType || redacted.has(e.eventId)) return false; + const relation = e.content["m.relates_to"] as + | { rel_type?: string; event_id?: string } + | undefined; + return relation?.rel_type === relType && relation.event_id === eventId; + }); + } + + // --- profile, media, capabilities -------------------------------------------- + + public getOwnProfile(): OwnProfile { + return this.ownProfile; + } + + public subscribeOwnProfile( + listener: (profile: OwnProfile) => void, + ): Unsubscribe { + return listen(this.profileListeners, listener); + } + + public setOwnProfile(profile: Partial): void { + this.ownProfile = { ...this.ownProfile, ...profile }; + for (const l of this.profileListeners) l(this.ownProfile); + } + + public async setDisplayName(name: string): Promise { + this.setOwnProfile({ displayName: name }); + return Promise.resolve(); + } + + public async thumbnailUrl( + mxcUrl: string, + width: number, + height: number, + _resizeMethod: "crop" | "scale", + ): Promise { + return Promise.resolve( + mxcUrl.startsWith("mxc://") + ? `https://media.example.org/thumbnail/${mxcUrl.slice("mxc://".length)}?width=${width}&height=${height}` + : null, + ); + } + + public async getCapabilities(): Promise { + return Promise.resolve(this.capabilities); + } + + public setCapabilities(capabilities: Partial): void { + this.capabilities = { ...this.capabilities, ...capabilities }; + } +} + +function listen(listeners: Set, listener: T): Unsubscribe { + listeners.add(listener); + return (): void => { + listeners.delete(listener); + }; +} diff --git a/src/driver/MockRtcMatrixDriver.test.ts b/src/driver/MockRtcMatrixDriver.test.ts new file mode 100644 index 000000000..39f87ce1c --- /dev/null +++ b/src/driver/MockRtcMatrixDriver.test.ts @@ -0,0 +1,171 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { beforeAll, describe, expect, it } from "vitest"; + +import { initMatrixRtcSdkForTests } from "../utils/test-matrix-rtc"; +import { + FfiElementCallCompat, + FfiMatrixDriver, + FfiMembershipState, + FfiParticipationManager, + FfiStatus, + FfiTransportIntent, + type FfiParticipationConfig, +} from "../matrix-rtc-sdk"; +import { + MOCK_LK_SERVICE_URL, + MOCK_OWN_USER_ID, + MOCK_SLOT_ID, + MockRtcMatrixDriver, + roomEncryptionEvent, + slotEvent, + waitFor, +} from "./MockRtcMatrixDriver"; + +const config: FfiParticipationConfig = { + compat: FfiElementCallCompat.StickyEvents, + manageMediaKeys: true, + requireCrossSignedSender: false, + useKeyDelayMs: 50n, +}; + +const joinParams = { + applicationType: "m.call", + intent: undefined, + stickyDurationMs: 240_000n, + keepAliveTimeoutMs: 15_000n, + degradedLifetimeMs: undefined, + delegateDelayedLeave: false, +}; + +const publish = (): FfiTransportIntent => + new FfiTransportIntent.Publish({ + transport: { + transportType: "livekit", + propertiesJson: JSON.stringify({ + livekit_service_url: MOCK_LK_SERVICE_URL, + }), + }, + }); + +function newManager(driver: MockRtcMatrixDriver): FfiParticipationManager { + return new FfiParticipationManager( + driver.roomId, + MOCK_SLOT_ID, + driver.userId, + driver.deviceId, + new FfiMatrixDriver(driver), + config, + ); +} + +describe("MockRtcMatrixDriver as the crate's driver", () => { + beforeAll(async () => { + await initMatrixRtcSdkForTests(); + }); + + it("echoes our sticky join so our own membership reaches the roster", async () => { + const driver = new MockRtcMatrixDriver({ + roomState: [slotEvent({ status: "open" })], + }); + const manager = newManager(driver); + await manager.join(publish(), joinParams); + expect(FfiStatus.Connected.instanceOf(manager.status())).toBe(true); + const sticky = driver.calls("stickyEvent"); + expect(sticky).toHaveLength(1); + expect(sticky[0].eventType).toBe("org.matrix.msc4143.rtc.member"); + const me = manager + .memberships() + .find((m) => m.member.userId === MOCK_OWN_USER_ID); + expect(me?.state).toBe(FfiMembershipState.Joined); + expect(me?.connections).toEqual([MOCK_LK_SERVICE_URL]); + expect(me?.transportIdentity).toBe(manager.ownTransportIdentity()); + // the token the mock minted is what the crate hands out + expect(manager.connections()[0].connection.jwtToken).toBe( + `jwt-for-${MOCK_LK_SERVICE_URL}`, + ); + await manager.leave(undefined, undefined); + manager.uniffiDestroy(); + }); + + it("hosts peers that join, answer our key and leave", async () => { + const driver = new MockRtcMatrixDriver({ + roomState: [ + roomEncryptionEvent(), + slotEvent({ status: "open", encrypted: true }), + ], + }); + const manager = newManager(driver); + const peer = driver.addPeer({ + userId: "@peer:example.org", + deviceId: "PEERDEV", + memberId: "m-peer", + }); + await manager.join( + new FfiTransportIntent.ReceiveOnly({ canSubscribe: ["livekit"] }), + joinParams, + ); + driver.peerJoins(peer); + expect(manager.memberships().map((m) => m.member.userId)).toContain( + peer.userId, + ); + await waitFor("key exchange", () => + manager.keyMap().some((k) => k.memberId === peer.memberId), + ); + // StickyEvents compat: our key went out in the deployed dialect + expect(driver.calls("toDevice")[0].eventType).toBe( + "io.element.call.encryption_keys", + ); + driver.peerLeaves(peer); + expect( + manager.memberships().find((m) => m.member.memberId === peer.memberId) + ?.state, + ).toBe(FfiMembershipState.LeftWithKeys); + await manager.leave(undefined, undefined); + manager.uniffiDestroy(); + }); + + it("reports homeserver connectivity into the crate", async () => { + const driver = new MockRtcMatrixDriver({ + roomState: [slotEvent({ status: "open" })], + }); + const manager = newManager(driver); + expect(manager.isHomeserverConnected()).toBe(true); + await manager.join(publish(), joinParams); + driver.setHomeserverConnected(false); + await waitFor("outage reported", () => !manager.isHomeserverConnected()); + await waitFor("impairment", () => { + const status = manager.status(); + return ( + FfiStatus.Connected.instanceOf(status) && + status.inner.impairments[0]?.tag === "HomeserverUnreachable" + ); + }); + driver.setHomeserverConnected(true); + await waitFor("outage clears", () => { + const status = manager.status(); + return ( + FfiStatus.Connected.instanceOf(status) && + status.inner.impairments.length === 0 + ); + }); + await manager.leave(undefined, undefined); + manager.uniffiDestroy(); + }); + + it("can refuse sticky and delayed events like an old homeserver", async () => { + const driver = new MockRtcMatrixDriver({ + roomState: [slotEvent({ status: "open" })], + }); + driver.refuseStickyEvents = true; + const manager = newManager(driver); + await expect(manager.join(publish(), joinParams)).rejects.toThrow(); + expect(FfiStatus.Disconnected.instanceOf(manager.status())).toBe(true); + manager.uniffiDestroy(); + }); +}); diff --git a/src/driver/MockRtcMatrixDriver.ts b/src/driver/MockRtcMatrixDriver.ts new file mode 100644 index 000000000..77f63ae57 --- /dev/null +++ b/src/driver/MockRtcMatrixDriver.ts @@ -0,0 +1,676 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +/** + * An {@link RtcMatrixDriver} with a homeserver made of arrays, for tests and + * stories. + * + * It models what sync would do: it records every outbound call, echoes + * accepted sticky and state events back through the room-event sink (so our + * own membership reaches the roster like anybody else's), answers `readState` + * from `roomState`, mints tokens, reports homeserver connectivity, and hosts + * simulated peers that answer our media key with theirs. + * + * A port of the crate's `web-test-app/src/mockDriver.ts`, which is the + * source of truth for the fabricated wire shapes below. + */ + +import { + FfiEventOrigin, + RtcError, + type ConnectivitySinkLike, + type FfiLivekitToken, + type FfiLivekitTokenRequest, + type FfiRtcTransport, + type FfiSendEventResponse, + type FfiToDeviceDelivery, + type FfiToDeviceRecipient, + type RoomEventSinkLike, + type StateUpdateSinkLike, + type ToDeviceSinkLike, +} from "../matrix-rtc-sdk"; +import { type RtcMatrixDriver } from "./RtcMatrixDriver"; + +export const MOCK_LK_SERVICE_URL = "https://lk.example.org"; +export const MOCK_ROOM_ID = "!room:example.org"; +/** MSC4143: a slot id is `{application_type}#{id}`; Element Call's is this. */ +export const MOCK_SLOT_ID = "m.call#ROOM"; +export const MOCK_OWN_USER_ID = "@me:example.org"; +export const MOCK_OWN_DEVICE_ID = "MYDEV"; + +/** Every call the crate made on the driver, in order. */ +export type OutboundCall = + | { + kind: "stickyEvent"; + roomId: string; + eventType: string; + content: Record; + durationMs: bigint; + } + | { + kind: "stateEvent"; + roomId: string; + eventType: string; + stateKey: string; + content: Record; + } + | { + kind: "delayedEvent"; + roomId: string; + eventType: string; + content: Record; + delayMs: bigint; + stickyDurationMs: bigint | undefined; + delayId: string; + } + | { + kind: "delayedStateEvent"; + roomId: string; + eventType: string; + stateKey: string; + content: Record; + delayMs: bigint; + delayId: string; + } + | { kind: "restartDelayed"; roomId: string; delayId: string } + | { kind: "cancelDelayed"; roomId: string; delayId: string } + | { + kind: "delegateDelayedLeave"; + roomId: string; + slotId: string; + delayId: string; + livekitServiceUrl: string | undefined; + delayMs: bigint; + } + | { + kind: "toDevice"; + recipients: FfiToDeviceRecipient[]; + eventType: string; + content: Record; + } + | { kind: "getRtcTransports" } + | { + kind: "getLivekitToken"; + url: string; + roomId: string; + slotId: string; + member: Record; + legacySfuGet: boolean; + }; + +/** A simulated remote participant. */ +export interface RemotePeer { + userId: string; + deviceId: string; + memberId: string; + /** 32 key bytes; defaults to a constant pattern. */ + key?: Uint8Array; +} + +/** A raw Matrix event as the crate reads it. */ +export type RawEvent = Record; + +export interface MockRtcMatrixDriverOptions { + userId?: string; + deviceId?: string; + roomId?: string; + /** Room state answered by `readState` (the crate's session seed). */ + roomState?: RawEvent[]; + /** Advertised by `getRtcTransports`; an empty list is "none". */ + transports?: FfiRtcTransport[]; +} + +export class MockRtcMatrixDriver implements RtcMatrixDriver { + /** Who this driver publishes as — not part of the contract, handy in tests. */ + public readonly userId: string; + public readonly deviceId: string; + public readonly roomId: string; + + public readonly outbound: OutboundCall[] = []; + /** Refuse delayed events like a homeserver without MSC4140 (404). */ + public refuseDelayedEvents = false; + /** Refuse sticky events like a homeserver without MSC4354 (404). */ + public refuseStickyEvents = false; + /** Make `getRtcTransports` fail rather than answer. */ + public failTransportDiscovery = false; + public roomState: RawEvent[]; + public transports: FfiRtcTransport[]; + /** Simulated peers answer our media key with theirs (index 0). */ + public readonly peers: RemotePeer[] = []; + + private roomEventSink?: RoomEventSinkLike; + private toDeviceSink?: ToDeviceSinkLike; + private stateUpdateSink?: StateUpdateSinkLike; + private connectivitySink?: ConnectivitySinkLike; + private homeserverConnected = true; + + private nextDelayId = 0; + private nextEventId = 0; + + public constructor(options: MockRtcMatrixDriverOptions = {}) { + this.userId = options.userId ?? MOCK_OWN_USER_ID; + this.deviceId = options.deviceId ?? MOCK_OWN_DEVICE_ID; + this.roomId = options.roomId ?? MOCK_ROOM_ID; + this.roomState = options.roomState ?? []; + this.transports = options.transports ?? [ + { + transportType: "livekit", + propertiesJson: JSON.stringify({ + livekit_service_url: MOCK_LK_SERVICE_URL, + }), + }, + ]; + } + + // --- assertions ---------------------------------------------------------- + + public calls( + kind: K, + ): Extract[] { + return this.outbound.filter((c) => c.kind === kind) as Extract< + OutboundCall, + { kind: K } + >[]; + } + + // --- outbound -------------------------------------------------------------- + + public async sendStickyEvent( + roomId: string, + eventType: string, + contentJson: string, + durationMs: bigint, + ): Promise { + const content = parse(contentJson); + this.record({ + kind: "stickyEvent", + roomId, + eventType, + content, + durationMs, + }); + if (this.refuseStickyEvents) + throw new RtcError.Unsupported( + "M_UNRECOGNIZED: sticky events are not supported", + ); + const eventId = this.eventId(); + // The homeserver echoes our event through sync. + this.echo( + { + type: eventType, + sender: this.userId, + event_id: eventId, + room_id: roomId, + origin_server_ts: Date.now(), + msc4354_sticky: { duration_ms: Number(durationMs) }, + content, + }, + new FfiEventOrigin.Encrypted({ senderDeviceId: this.deviceId }), + ); + return Promise.resolve({ eventId, delayId: undefined }); + } + + public async sendStateEvent( + roomId: string, + eventType: string, + stateKey: string, + contentJson: string, + ): Promise { + const content = parse(contentJson); + this.record({ kind: "stateEvent", roomId, eventType, stateKey, content }); + const eventId = this.eventId(); + this.echo( + { + type: eventType, + sender: this.userId, + event_id: eventId, + room_id: roomId, + state_key: stateKey, + origin_server_ts: Date.now(), + content, + }, + new FfiEventOrigin.Cleartext(), + ); + return Promise.resolve({ eventId, delayId: undefined }); + } + + public async sendDelayedEvent( + roomId: string, + eventType: string, + contentJson: string, + delayMs: bigint, + stickyDurationMs: bigint | undefined, + ): Promise { + const delayId = `delay-${this.nextDelayId++}`; + this.record({ + kind: "delayedEvent", + roomId, + eventType, + content: parse(contentJson), + delayMs, + stickyDurationMs, + delayId, + }); + if (this.refuseDelayedEvents) + // 404 M_UNRECOGNIZED: "this homeserver will never do delayed events". + throw new RtcError.Unsupported( + "M_UNRECOGNIZED: delayed events are not supported", + ); + return Promise.resolve(delayId); + } + + public async sendDelayedStateEvent( + roomId: string, + eventType: string, + stateKey: string, + contentJson: string, + delayMs: bigint, + ): Promise { + const delayId = `delay-${this.nextDelayId++}`; + this.record({ + kind: "delayedStateEvent", + roomId, + eventType, + stateKey, + content: parse(contentJson), + delayMs, + delayId, + }); + return Promise.resolve(delayId); + } + + public async restartDelayedEvent( + roomId: string, + delayId: string, + ): Promise { + this.record({ kind: "restartDelayed", roomId, delayId }); + return Promise.resolve(); + } + + public async cancelDelayedEvent( + roomId: string, + delayId: string, + ): Promise { + this.record({ kind: "cancelDelayed", roomId, delayId }); + return Promise.resolve(); + } + + public async delegateLivekitDelayedLeave( + roomId: string, + slotId: string, + _memberJson: string, + delayId: string, + livekitServiceUrl: string | undefined, + delayMs: bigint, + ): Promise { + this.record({ + kind: "delegateDelayedLeave", + roomId, + slotId, + delayId, + livekitServiceUrl, + delayMs, + }); + return Promise.resolve(); + } + + public async sendToDevice( + recipients: FfiToDeviceRecipient[], + eventType: string, + contentJson: string, + ): Promise { + this.record({ + kind: "toDevice", + recipients, + eventType, + content: parse(contentJson), + }); + // Simulated peers answer with their own key. + for (const recipient of recipients) { + const peer = this.peers.find( + (p) => + p.userId === recipient.userId && p.deviceId === recipient.deviceId, + ); + if (peer) queueMicrotask(() => this.peerSendsKey(peer, 0)); + } + // every recipient reachable + return Promise.resolve( + recipients.map((recipient) => ({ recipient, error: undefined })), + ); + } + + public async getRtcTransports(): Promise { + this.record({ kind: "getRtcTransports" }); + if (this.failTransportDiscovery) + throw new RtcError.Http("500: transports endpoint unavailable"); + return Promise.resolve(this.transports); + } + + public async getLivekitToken( + request: FfiLivekitTokenRequest, + ): Promise { + this.record({ + kind: "getLivekitToken", + url: request.url, + roomId: request.roomId, + slotId: request.slotId, + member: parse(request.memberJson), + legacySfuGet: request.legacySfuGet, + }); + return Promise.resolve({ + jwt: "jwt-for-" + request.url, + url: request.url.replace("https", "wss"), + }); + } + + public async readEvents(): Promise { + return Promise.resolve([]); + } + + public async readState( + eventType: string, + stateKey: string | undefined, + ): Promise { + return Promise.resolve( + this.roomState + .filter( + (e) => + e.type === eventType && + (stateKey === undefined || e.state_key === stateKey), + ) + .map((e) => JSON.stringify(e)), + ); + } + + // --- inbound sinks ----------------------------------------------------------- + // The crate subscribes exactly once, when its FfiMatrixDriver is built, and + // hands over sinks; a real driver hooks client listeners onto them. The + // mock stores them so tests can emit fabricated events. + + public subscribeRoomEvents(sink: RoomEventSinkLike): void { + this.roomEventSink = sink; + } + + public subscribeToDeviceEvents(sink: ToDeviceSinkLike): void { + this.toDeviceSink = sink; + } + + public subscribeStateUpdates(sink: StateUpdateSinkLike): void { + this.stateUpdateSink = sink; + } + + public subscribeConnectivity(sink: ConnectivitySinkLike): void { + this.connectivitySink = sink; + } + + public isHomeserverConnected(): boolean { + return this.homeserverConnected; + } + + /** The homeserver comes or goes, as a syncing client would report it. */ + public setHomeserverConnected(connected: boolean): void { + this.homeserverConnected = connected; + this.connectivitySink?.emit(connected); + } + + /** Emit any room event — sticky or state; the crate dispatches on type. */ + public emitRoomEvent(event: RawEvent, origin: FfiEventOrigin): boolean { + if (!this.roomEventSink) + throw new Error("The SDK has not subscribed to room events"); + return this.roomEventSink.emit(JSON.stringify(event), origin); + } + + /** `senderCrossSigned` is the MSC4153 verdict; peers are cross-signed by default. */ + public emitToDevice( + eventType: string, + sender: string, + content: RawEvent, + origin: FfiEventOrigin, + senderCrossSigned: boolean | undefined = true, + ): boolean { + if (!this.toDeviceSink) + throw new Error("The SDK has not subscribed to to-device events"); + return this.toDeviceSink.emit( + eventType, + sender, + JSON.stringify(content), + origin, + senderCrossSigned, + ); + } + + public emitStateUpdate(events: RawEvent[]): boolean { + if (!this.stateUpdateSink) + throw new Error("The SDK has not subscribed to state updates"); + return this.stateUpdateSink.emit(events.map((e) => JSON.stringify(e))); + } + + // --- simulated peers -------------------------------------------------------- + + public addPeer(peer: RemotePeer): RemotePeer { + this.peers.push(peer); + return peer; + } + + /** The peer publishes a join (on `MOCK_LK_SERVICE_URL` unless given). */ + public peerJoins( + peer: RemotePeer, + opts: { lkServiceUrl?: string; durationMs?: number } = {}, + ): boolean { + return this.emitRoomEvent( + memberJoinEvent({ + roomId: this.roomId, + userId: peer.userId, + memberId: peer.memberId, + ...opts, + }), + new FfiEventOrigin.Encrypted({ senderDeviceId: peer.deviceId }), + ); + } + + public peerLeaves(peer: RemotePeer): boolean { + return this.emitRoomEvent( + memberLeaveEvent({ + roomId: this.roomId, + userId: peer.userId, + memberId: peer.memberId, + }), + new FfiEventOrigin.Encrypted({ senderDeviceId: peer.deviceId }), + ); + } + + public peerSendsKey(peer: RemotePeer, index: number): boolean { + return this.emitToDevice( + "m.rtc.encryption_key", + peer.userId, + encryptionKeyContent({ + roomId: this.roomId, + memberId: peer.memberId, + index, + key: peer.key, + }), + new FfiEventOrigin.Encrypted({ senderDeviceId: peer.deviceId }), + ); + } + + // --- internals ----------------------------------------------------------------- + + private record(call: OutboundCall): void { + this.outbound.push(call); + } + + private eventId(): string { + return `$echo-${this.nextEventId++}`; + } + + private echo(event: RawEvent, origin: FfiEventOrigin): void { + this.roomEventSink?.emit(JSON.stringify(event), origin); + } +} + +function parse(json: string): Record { + return JSON.parse(json) as Record; +} + +// --------------------------------------------------------------------------- +// Inbound event fabrication — the MSC4143/MSC4354 wire shapes the crate's +// dispatch reads (see its src/session/dispatch.rs). Adjust here, not in +// every test. +// --------------------------------------------------------------------------- + +let eventCounter = 0; + +export function memberJoinEvent(opts: { + roomId?: string; + userId: string; + memberId: string; + lkServiceUrl?: string; + durationMs?: number; +}): RawEvent { + return { + type: "m.rtc.member", + sender: opts.userId, + event_id: `$ev-${eventCounter++}`, + room_id: opts.roomId ?? MOCK_ROOM_ID, + origin_server_ts: Date.now(), + msc4354_sticky: { duration_ms: opts.durationMs ?? 240_000 }, + content: { + slot_id: MOCK_SLOT_ID, + // MSC4354: the sticky key lives in the content and equals member.id. + msc4354_sticky_key: opts.memberId, + member: { id: opts.memberId, membership: "join" }, + application: { type: "m.call" }, + transports: { + published: [ + { + type: "livekit", + livekit_service_url: opts.lkServiceUrl ?? MOCK_LK_SERVICE_URL, + }, + ], + can_subscribe: ["livekit"], + }, + }, + }; +} + +export function memberLeaveEvent(opts: { + roomId?: string; + userId: string; + memberId: string; +}): RawEvent { + return { + type: "m.rtc.member", + sender: opts.userId, + event_id: `$ev-${eventCounter++}`, + room_id: opts.roomId ?? MOCK_ROOM_ID, + origin_server_ts: Date.now(), + msc4354_sticky: { duration_ms: 240_000 }, + content: { + slot_id: MOCK_SLOT_ID, + msc4354_sticky_key: opts.memberId, + member: { id: opts.memberId, membership: "leave" }, + leave_reason: { code: "leave" }, + }, + }; +} + +export function slotEvent( + opts: { roomId?: string; status: "open" | "closed"; encrypted?: boolean } = { + status: "open", + }, +): RawEvent { + const content: Record = { + status: opts.status, + application: { type: "m.call" }, + }; + if (opts.encrypted) content.encryption = { type: "m.per_member" }; + return { + type: "m.rtc.slot", + sender: "@admin:example.org", + event_id: `$ev-${eventCounter++}`, + room_id: opts.roomId ?? MOCK_ROOM_ID, + state_key: MOCK_SLOT_ID, + origin_server_ts: Date.now(), + content, + }; +} + +export function roomEncryptionEvent(roomId = MOCK_ROOM_ID): RawEvent { + return { + type: "m.room.encryption", + sender: "@admin:example.org", + event_id: `$ev-${eventCounter++}`, + room_id: roomId, + state_key: "", + origin_server_ts: Date.now(), + content: { algorithm: "m.megolm.v1.aes-sha2" }, + }; +} + +/** `m.room.member` state with the profile fields the crate puts on a member. */ +export function roomMemberEvent(opts: { + roomId?: string; + userId: string; + membership?: "join" | "invite" | "leave"; + displayName?: string; + avatarUrl?: string; +}): RawEvent { + const content: Record = { + membership: opts.membership ?? "join", + }; + if (opts.displayName !== undefined) content.displayname = opts.displayName; + if (opts.avatarUrl !== undefined) content.avatar_url = opts.avatarUrl; + return { + type: "m.room.member", + sender: opts.userId, + event_id: `$ev-${eventCounter++}`, + room_id: opts.roomId ?? MOCK_ROOM_ID, + state_key: opts.userId, + origin_server_ts: Date.now(), + content, + }; +} + +const DEFAULT_KEY = new Uint8Array(32).fill(7); + +function base64(bytes: Uint8Array): string { + let binary = ""; + for (const b of bytes) binary += String.fromCharCode(b); + return btoa(binary).replace(/=+$/, ""); +} + +/** MSC4143 `m.rtc.encryption_key` content. */ +export function encryptionKeyContent(opts: { + roomId?: string; + memberId: string; + index: number; + key?: Uint8Array; +}): RawEvent { + return { + room_id: opts.roomId ?? MOCK_ROOM_ID, + member_id: opts.memberId, + media_key: { index: opts.index, key: base64(opts.key ?? DEFAULT_KEY) }, + format: 0, + }; +} + +/** One timer tick: the crate's listener callbacks arrive after the emitting task yields. */ +export const tick = async (): Promise => + new Promise((resolve) => setTimeout(resolve, 0)); + +export async function waitFor( + what: string, + cond: () => boolean, + timeoutMs = 3000, +): Promise { + const deadline = Date.now() + timeoutMs; + while (!cond()) { + if (Date.now() > deadline) + throw new Error(`Timed out waiting for: ${what}`); + await new Promise((r) => setTimeout(r, 10)); + } +} diff --git a/src/driver/RtcMatrixDriver.ts b/src/driver/RtcMatrixDriver.ts new file mode 100644 index 000000000..19bd1026c --- /dev/null +++ b/src/driver/RtcMatrixDriver.ts @@ -0,0 +1,21 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +/** + * The MatrixRTC half of what a host supplies: the `matrix-rtc` crate's own + * driver contract, taken verbatim from its bindings. Everything MatrixRTC — + * sticky and delayed events, to-device key delivery, transport tokens, the + * inbound event sinks and homeserver connectivity — goes through this and + * is consumed by the crate, never by Element Call directly. + * + * What a call needs from a Matrix client beyond MatrixRTC is the separate + * {@link ElementCallMatrixClientDriver}. + */ + +import { type MatrixDriverCallback } from "../matrix-rtc-sdk"; + +export type RtcMatrixDriver = MatrixDriverCallback; diff --git a/src/driver/jsSdk/JsSdkElementCallMatrixClientDriver.test.ts b/src/driver/jsSdk/JsSdkElementCallMatrixClientDriver.test.ts new file mode 100644 index 000000000..cabb136d8 --- /dev/null +++ b/src/driver/jsSdk/JsSdkElementCallMatrixClientDriver.test.ts @@ -0,0 +1,123 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { MatrixEvent, RoomEvent, UserEvent } from "matrix-js-sdk"; +import { describe, expect, it, vi } from "vitest"; + +import { JsSdkElementCallMatrixClientDriver } from "./JsSdkElementCallMatrixClientDriver"; +import { + ME, + ROOM_ID, + asClient, + asRoom, + fakeClient, + fakeRoom, +} from "./jsSdkTestFakes"; + +describe("JsSdkElementCallMatrixClientDriver", () => { + it("serves room info, members, timeline and profile from a full client", async () => { + const client = fakeClient(false); + const room = fakeRoom(); + const driver = new JsSdkElementCallMatrixClientDriver( + asClient(client), + asRoom(room), + ); + expect(driver.userId).toBe(ME); + expect(driver.getRoomInfo()).toEqual({ + name: "Standup", + canonicalAlias: "#standup:example.org", + avatarUrl: "mxc://example.org/room", + joinRule: "public", + encrypted: true, + canOpenSlot: true, + }); + const onInfo = vi.fn(); + const offInfo = driver.subscribeRoomInfo(onInfo); + room.name = "Retro"; + room.emit(RoomEvent.Name, room); + expect(onInfo).toHaveBeenCalledWith( + expect.objectContaining({ name: "Retro" }), + ); + offInfo(); + + expect(driver.getRoomMembers()).toEqual([ + { + userId: "@a:example.org", + displayName: "Alice", + avatarUrl: "mxc://example.org/alice", + membership: "join", + }, + { + userId: "@b:example.org", + displayName: null, + avatarUrl: null, + membership: "invite", + }, + ]); + + const seen: string[] = []; + driver.subscribeTimeline((e) => seen.push(`${e.type}:${e.eventId}`)); + const reaction = new MatrixEvent({ + type: "m.reaction", + sender: "@a:example.org", + event_id: "$r1", + room_id: ROOM_ID, + origin_server_ts: 3, + content: { "m.relates_to": { rel_type: "m.annotation", key: "🖐️" } }, + }); + client.emit(RoomEvent.Timeline, reaction, room, false, false, {}); + client.emit(RoomEvent.Timeline, reaction, room, false, false, {}); + await vi.waitFor(() => expect(seen).toEqual(["m.reaction:$r1"])); + + await driver.sendRoomEvent("io.element.call.reaction", { emoji: "🎉" }); + expect(client.sendEvent).toHaveBeenCalledWith( + ROOM_ID, + "io.element.call.reaction", + { emoji: "🎉" }, + ); + await driver.redactEvent("$r1"); + expect(client.redactEvent).toHaveBeenCalledWith(ROOM_ID, "$r1"); + + expect(driver.getOwnProfile()).toEqual({ + displayName: "Me", + avatarUrl: "mxc://example.org/me", + }); + const onProfile = vi.fn(); + driver.subscribeOwnProfile(onProfile); + const user = client.getUser(ME)!; + user.rawDisplayName = "Moi"; + user.emit(UserEvent.DisplayName, undefined, user); + expect(onProfile).toHaveBeenCalledWith( + expect.objectContaining({ displayName: "Moi" }), + ); + + await expect(driver.getCapabilities()).resolves.toEqual({ + stickyEvents: true, + verifiedEventOrigins: true, + crossSigningVerdicts: true, + }); + await expect(driver.getDiagnostics()).resolves.toMatchObject({ + matrix_backend: "jssdk", + crypto_version: "fake 1.0", + }); + }); + + it("says what a widget client cannot vouch for and leaves media to the host bridge", async () => { + const driver = new JsSdkElementCallMatrixClientDriver( + asClient(fakeClient(true)), + asRoom(fakeRoom()), + ); + await expect(driver.getCapabilities()).resolves.toMatchObject({ + verifiedEventOrigins: false, + crossSigningVerdicts: false, + }); + // no token of its own: media comes through the host bridge instead + await expect( + driver.thumbnailUrl("mxc://example.org/x", 96, 96, "crop"), + ).resolves.toBeNull(); + }); +}); diff --git a/src/driver/jsSdk/JsSdkElementCallMatrixClientDriver.ts b/src/driver/jsSdk/JsSdkElementCallMatrixClientDriver.ts new file mode 100644 index 000000000..32b6bbc76 --- /dev/null +++ b/src/driver/jsSdk/JsSdkElementCallMatrixClientDriver.ts @@ -0,0 +1,329 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +/** + * An {@link ElementCallMatrixClientDriver} over a matrix-js-sdk client: room + * metadata and members, the room's timeline for reactions and notifications, + * the user's own profile, authenticated thumbnails and capability probes. + * Works on a full `MatrixClient` and on a `RoomWidgetClient`; the places + * they differ are marked "widget". + */ + +import { + KnownMembership, + type MatrixClient, + type MatrixEvent, + type Room, + RoomEvent, + type RoomMember, + RoomStateEvent, + RoomWidgetClient, + UNSTABLE_MSC4354_STICKY_EVENTS, + type User, + UserEvent, +} from "matrix-js-sdk"; +import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger"; + +import { ELEMENT_CALL_SLOT_EVENT_TYPE } from "../../state/rtc/slot"; +import { + type DriverCapabilities, + type ElementCallMatrixClientDriver, + type OwnProfile, + type RoomInfo, + type RoomMemberProfile, + type TimelineEvent, + type Unsubscribe, +} from "../ElementCallMatrixClientDriver"; + +/** The state event types `getRoomInfo()` is computed from. */ +const ROOM_INFO_EVENT_TYPES = new Set([ + "m.room.name", + "m.room.avatar", + "m.room.canonical_alias", + "m.room.join_rules", + "m.room.encryption", + "m.room.power_levels", +]); + +export interface JsSdkElementCallMatrixClientDriverOptions { + logger?: Logger; +} + +export class JsSdkElementCallMatrixClientDriver implements ElementCallMatrixClientDriver { + public readonly userId: string; + public readonly deviceId: string; + public readonly roomId: string; + + private readonly logger: Logger; + /** Widget: no crypto backend, no access token, events without metadata. */ + private readonly widget: boolean; + private capabilities: Promise | null = null; + + public constructor( + private readonly client: MatrixClient, + private readonly room: Room, + options: JsSdkElementCallMatrixClientDriverOptions = {}, + ) { + const userId = client.getUserId(); + const deviceId = client.getDeviceId(); + if (userId === null || deviceId === null) + throw new Error( + "The client must be logged in before it can drive a call", + ); + this.userId = userId; + this.deviceId = deviceId; + this.roomId = room.roomId; + this.widget = client instanceof RoomWidgetClient; + this.logger = (options.logger ?? rootLogger).getChild( + `[JsSdkElementCallMatrixClientDriver ${room.roomId}]`, + ); + } + + // --- room ------------------------------------------------------------------ + + public getRoomInfo(): RoomInfo { + return { + name: this.room.name, + canonicalAlias: this.room.getCanonicalAlias(), + avatarUrl: this.room.getMxcAvatarUrl(), + joinRule: this.room.currentState.getJoinRule() ?? null, + encrypted: this.room.hasEncryptionStateEvent(), + canOpenSlot: this.room.currentState.maySendStateEvent( + ELEMENT_CALL_SLOT_EVENT_TYPE, + this.userId, + ), + }; + } + + public subscribeRoomInfo(listener: (info: RoomInfo) => void): Unsubscribe { + const notify = (): void => listener(this.getRoomInfo()); + const onState = (event: MatrixEvent): void => { + if ( + event.getRoomId() === this.roomId && + ROOM_INFO_EVENT_TYPES.has(event.getType()) + ) + notify(); + }; + this.room.on(RoomEvent.Name, notify); + this.client.on(RoomStateEvent.Events, onState); + return (): void => { + this.room.off(RoomEvent.Name, notify); + this.client.off(RoomStateEvent.Events, onState); + }; + } + + public getRoomMembers(): RoomMemberProfile[] { + const profile = + (membership: "join" | "invite") => + (member: RoomMember): RoomMemberProfile => ({ + userId: member.userId, + displayName: member.rawDisplayName ?? null, + avatarUrl: member.getMxcAvatarUrl() ?? null, + membership, + }); + return [ + ...this.room + .getMembersWithMembership(KnownMembership.Join) + .map(profile("join")), + ...this.room + .getMembersWithMembership(KnownMembership.Invite) + .map(profile("invite")), + ]; + } + + public subscribeRoomMembers( + listener: (members: RoomMemberProfile[]) => void, + ): Unsubscribe { + const onMembers = (event: MatrixEvent): void => { + if (event.getRoomId() === this.roomId) listener(this.getRoomMembers()); + }; + this.client.on(RoomStateEvent.Members, onMembers); + return (): void => { + this.client.off(RoomStateEvent.Members, onMembers); + }; + } + + // --- timeline ---------------------------------------------------------------- + + public async sendRoomEvent( + eventType: string, + content: Record, + ): Promise<{ eventId: string }> { + const res = await this.client.sendEvent( + this.roomId, + eventType as never, + content as never, + ); + return { eventId: res.event_id }; + } + + public async redactEvent(eventId: string): Promise { + await this.client.redactEvent(this.roomId, eventId); + } + + public subscribeTimeline( + listener: (event: TimelineEvent) => void, + ): Unsubscribe { + // Our own events are seen twice — as the local echo, then as sent — + // and a redaction is both a timeline event and a Redaction signal. + const seen = new Set(); + const deliver = async (event: MatrixEvent): Promise => { + if (event.getRoomId() !== this.roomId) return; + // Still sending: the LocalEchoUpdated listener gets the real id later. + if (event.status !== null) return; + if (event.unstableStickyInfo !== undefined || event.isState()) return; + const eventId = event.getId(); + const sender = event.getSender(); + if (!eventId || !sender || seen.has(eventId)) return; + try { + await this.client.decryptEventIfNeeded(event); + } catch (e) { + this.logger.warn(`Could not decrypt ${eventId}`, e); + } + if (event.isDecryptionFailure() || seen.has(eventId)) return; + seen.add(eventId); + if (seen.size > 1000) seen.delete(seen.values().next().value!); + listener({ + eventId, + type: event.getType(), + sender, + content: event.getContent(), + originServerTs: event.getTs(), + redacts: event.event.redacts, + }); + }; + const onTimeline = ( + event: MatrixEvent, + room: Room | undefined, + toStartOfTimeline: boolean | undefined, + ): void => { + if (room?.roomId === this.roomId && !toStartOfTimeline) + void deliver(event); + }; + const onEcho = (event: MatrixEvent): void => void deliver(event); + this.client.on(RoomEvent.Timeline, onTimeline); + this.room.on(RoomEvent.LocalEchoUpdated, onEcho); + this.room.on(RoomEvent.Redaction, onEcho); + return (): void => { + this.client.off(RoomEvent.Timeline, onTimeline); + this.room.off(RoomEvent.LocalEchoUpdated, onEcho); + this.room.off(RoomEvent.Redaction, onEcho); + }; + } + + public getRelatedEvents( + eventId: string, + relType: string, + eventType: string, + ): TimelineEvent[] { + const relations = this.room.relations.getChildEventsForEvent( + eventId, + relType as never, + eventType as never, + ); + return (relations?.getRelations() ?? []) + .filter( + (event) => !event.isRedacted() && event.getId() && event.getSender(), + ) + .map((event) => ({ + eventId: event.getId()!, + type: event.getType(), + sender: event.getSender()!, + content: event.getContent(), + originServerTs: event.getTs(), + })); + } + + // --- profile ------------------------------------------------------------------- + + public getOwnProfile(): OwnProfile { + const user = this.client.getUser(this.userId); + return { + displayName: user?.rawDisplayName ?? null, + avatarUrl: user?.avatarUrl ?? null, + }; + } + + public subscribeOwnProfile( + listener: (profile: OwnProfile) => void, + ): Unsubscribe { + const user: User | null = this.client.getUser(this.userId); + if (user === null) return (): void => {}; + const notify = (): void => listener(this.getOwnProfile()); + user.on(UserEvent.DisplayName, notify); + user.on(UserEvent.AvatarUrl, notify); + return (): void => { + user.off(UserEvent.DisplayName, notify); + user.off(UserEvent.AvatarUrl, notify); + }; + } + + public async setDisplayName(name: string): Promise { + await this.client.setDisplayName(name); + } + + public async setAvatar(file: Blob): Promise { + const { content_uri: uri } = await this.client.uploadContent(file); + await this.client.setAvatarUrl(uri); + } + + // --- media ----------------------------------------------------------------------- + + public async thumbnailUrl( + mxcUrl: string, + width: number, + height: number, + resizeMethod: "crop" | "scale", + ): Promise { + const httpUrl = this.client.mxcUrlToHttp( + mxcUrl, + width, + height, + resizeMethod, + false, + true, + true, + ); + // Widget: no token of our own; the host bridge downloads media instead. + const token = this.client.getAccessToken(); + if (httpUrl === null || token === null) return null; + const response = await fetch(httpUrl, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (!response.ok) return null; + return URL.createObjectURL(await response.blob()); + } + + // --- capabilities and diagnostics ------------------------------------------------ + + public async getCapabilities(): Promise { + this.capabilities ??= this.probeCapabilities(); + return this.capabilities; + } + + private async probeCapabilities(): Promise { + const stickyEvents = await this.client + .doesServerSupportUnstableFeature(UNSTABLE_MSC4354_STICKY_EVENTS) + .catch((e: unknown) => { + this.logger.warn("Could not probe sticky event support", e); + return false; + }); + return { + stickyEvents, + verifiedEventOrigins: !this.widget, + crossSigningVerdicts: this.client.getCrypto() !== undefined, + }; + } + + public async getDiagnostics(): Promise> { + return Promise.resolve({ + matrix_backend: this.widget ? "widget" : "jssdk", + crypto_version: this.client.getCrypto()?.getVersion() ?? "none", + sync_state: String(this.client.getSyncState()), + }); + } +} diff --git a/src/driver/jsSdk/JsSdkRtcMatrixDriver.test.ts b/src/driver/jsSdk/JsSdkRtcMatrixDriver.test.ts new file mode 100644 index 000000000..130fbfb98 --- /dev/null +++ b/src/driver/jsSdk/JsSdkRtcMatrixDriver.test.ts @@ -0,0 +1,488 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { + ClientEvent, + MatrixError, + MatrixEvent, + RoomStateEvent, + RoomStickyEventsEvent, + SyncState, + UnsupportedStickyEventsEndpointError, + UpdateDelayedEventAction, +} from "matrix-js-sdk"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import { initMatrixRtcSdkForTests } from "../../utils/test-matrix-rtc"; +import { + FfiEventOrigin, + RtcError, + type ConnectivitySinkLike, + type RoomEventSinkLike, + type StateUpdateSinkLike, + type ToDeviceSinkLike, +} from "../../matrix-rtc-sdk"; +import { JsSdkRtcMatrixDriver } from "./JsSdkRtcMatrixDriver"; +import { + LK, + ME, + MY_DEVICE, + ROOM_ID, + asClient, + asRoom, + fakeClient, + fakeRoom, + jsonResponse, + openIdToken, + type FakeClient, + type FakeRoom, +} from "./jsSdkTestFakes"; + +const memberJson = JSON.stringify({ + id: "m-1", + claimed_user_id: ME, + claimed_device_id: MY_DEVICE, +}); + +let fetchMock: ReturnType>; + +describe("JsSdkRtcMatrixDriver", () => { + beforeEach(async () => { + // The bindings define the RtcError classes the driver throws. + await initMatrixRtcSdkForTests(); + fetchMock = vi.fn(); + vi.stubGlobal("fetch", fetchMock); + }); + afterEach(() => { + vi.unstubAllGlobals(); + }); + + describe("over a full MatrixClient", () => { + it("sends sticky, delayed and state events through the unstable APIs", async () => { + const { client, driver } = fullClient(); + await expect( + driver.sendStickyEvent(ROOM_ID, "m.rtc.member", '{"a":1}', 240_000n), + ).resolves.toEqual({ eventId: "$sticky", delayId: undefined }); + expect(client._unstable_sendStickyEvent).toHaveBeenCalledWith( + ROOM_ID, + 240_000, + null, + "m.rtc.member", + { a: 1 }, + ); + + await expect( + driver.sendDelayedEvent( + ROOM_ID, + "m.rtc.member", + "{}", + 15_000n, + 240_000n, + ), + ).resolves.toBe("delay-sticky"); + expect(client._unstable_sendStickyDelayedEvent).toHaveBeenCalledWith( + ROOM_ID, + 240_000, + { delay: 15_000 }, + null, + "m.rtc.member", + {}, + ); + await expect( + driver.sendDelayedEvent( + ROOM_ID, + "m.rtc.member", + "{}", + 15_000n, + undefined, + ), + ).resolves.toBe("delay-plain"); + expect(client._unstable_sendDelayedEvent).toHaveBeenCalledWith( + ROOM_ID, + { delay: 15_000 }, + null, + "m.rtc.member", + {}, + ); + + await driver.sendDelayedStateEvent(ROOM_ID, "m.x", "key", "{}", 5_000n); + expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledWith( + ROOM_ID, + { delay: 5_000 }, + "m.x", + {}, + "key", + ); + await driver.sendStateEvent(ROOM_ID, "m.rtc.slot", "m.call#ROOM", "{}"); + expect(client.sendStateEvent).toHaveBeenCalledWith( + ROOM_ID, + "m.rtc.slot", + {}, + "m.call#ROOM", + ); + + await driver.restartDelayedEvent(ROOM_ID, "d1"); + await driver.cancelDelayedEvent(ROOM_ID, "d1"); + expect(client._unstable_updateDelayedEvent).toHaveBeenNthCalledWith( + 1, + "d1", + UpdateDelayedEventAction.Restart, + ); + expect(client._unstable_updateDelayedEvent).toHaveBeenNthCalledWith( + 2, + "d1", + UpdateDelayedEventAction.Cancel, + ); + }); + + it("maps js-sdk failures onto the crate's error family", async () => { + const { client, driver } = fullClient(); + client._unstable_sendStickyEvent.mockRejectedValueOnce( + new UnsupportedStickyEventsEndpointError("nope", "sendStickyEvent"), + ); + await expect( + driver.sendStickyEvent(ROOM_ID, "m.rtc.member", "{}", 1n), + ).rejects.toSatisfy((e) => RtcError.Unsupported.instanceOf(e)); + + client.sendStateEvent.mockRejectedValueOnce( + new MatrixError({ errcode: "M_FORBIDDEN" }, 403), + ); + await expect( + driver.sendStateEvent(ROOM_ID, "m.rtc.slot", "k", "{}"), + ).rejects.toSatisfy((e) => RtcError.Rejected.instanceOf(e)); + + client.sendStateEvent.mockRejectedValueOnce( + new MatrixError( + { errcode: "M_LIMIT_EXCEEDED", retry_after_ms: 1500 }, + 429, + ), + ); + await expect( + driver.sendStateEvent(ROOM_ID, "m.rtc.slot", "k", "{}"), + ).rejects.toSatisfy( + (e) => + RtcError.RateLimited.instanceOf(e) && e.inner.retryAfterMs === 1500n, + ); + }); + + it("sends to-device messages Olm-encrypted per device", async () => { + const { client, driver } = fullClient(); + const recipients = [{ userId: "@a:example.org", deviceId: "ADEV" }]; + await expect( + driver.sendToDevice(recipients, "m.rtc.encryption_key", '{"k":1}'), + ).resolves.toEqual([{ recipient: recipients[0], error: undefined }]); + expect(client.encryptAndSendToDevice).toHaveBeenCalledWith( + "m.rtc.encryption_key", + recipients, + { k: 1 }, + ); + }); + + it("discovers transports through the client and answers in the crate's shape", async () => { + const { client, driver } = fullClient(); + client._unstable_getRTCTransports.mockResolvedValue([ + { type: "livekit", livekit_service_url: LK }, + ]); + await expect(driver.getRtcTransports()).resolves.toEqual([ + { + transportType: "livekit", + propertiesJson: JSON.stringify({ livekit_service_url: LK }), + }, + ]); + }); + + it("exchanges an OpenID token for a LiveKit token, on either endpoint", async () => { + const { driver } = fullClient(); + // a fresh Response per call: a body can be read once + fetchMock.mockImplementation(async () => + Promise.resolve(jsonResponse({ jwt: "the-jwt", url: "wss://sfu" })), + ); + await expect( + driver.getLivekitToken({ + url: LK, + roomId: ROOM_ID, + slotId: "m.call#ROOM", + memberJson, + legacySfuGet: false, + }), + ).resolves.toEqual({ jwt: "the-jwt", url: "wss://sfu" }); + const [endpoint, init] = fetchMock.mock.calls[0] as unknown as [ + string, + RequestInit, + ]; + expect(endpoint).toBe(`${LK}/get_token`); + expect(JSON.parse(init.body as string)).toEqual({ + room_id: ROOM_ID, + slot_id: "m.call#ROOM", + openid_token: openIdToken, + member: JSON.parse(memberJson), + }); + + await driver.getLivekitToken({ + url: LK, + roomId: ROOM_ID, + slotId: "m.call#ROOM", + memberJson, + legacySfuGet: true, + }); + const [legacyEndpoint, legacyInit] = fetchMock.mock + .calls[1] as unknown as [string, RequestInit]; + expect(legacyEndpoint).toBe(`${LK}/sfu/get`); + expect(JSON.parse(legacyInit.body as string)).toEqual({ + room: ROOM_ID, + openid_token: openIdToken, + device_id: MY_DEVICE, + }); + + fetchMock.mockResolvedValueOnce(new Response("gone", { status: 404 })); + await expect( + driver.getLivekitToken({ + url: LK, + roomId: ROOM_ID, + slotId: "m.call#ROOM", + memberJson, + legacySfuGet: false, + }), + ).rejects.toSatisfy((e) => RtcError.Unsupported.instanceOf(e)); + }); + + it("delegates the delayed leave through the authorisation service's token endpoint", async () => { + const { driver } = fullClient(); + fetchMock.mockImplementation(async () => + Promise.resolve(jsonResponse({ jwt: "discarded" })), + ); + await driver.delegateLivekitDelayedLeave( + ROOM_ID, + "m.call#ROOM", + memberJson, + "delay-1", + LK, + 3_600_000n, + ); + const [endpoint, init] = fetchMock.mock.calls[0] as unknown as [ + string, + RequestInit, + ]; + expect(endpoint).toBe(`${LK}/get_token`); + expect(JSON.parse(init.body as string)).toMatchObject({ + delay_id: "delay-1", + delay_timeout: 3_600_000, + delay_cs_api_url: "https://hs.example.org", + }); + // receive-only: nothing to delegate to + await expect( + driver.delegateLivekitDelayedLeave( + ROOM_ID, + "m.call#ROOM", + memberJson, + "delay-1", + undefined, + 1n, + ), + ).rejects.toSatisfy((e) => RtcError.Unsupported.instanceOf(e)); + }); + + it("feeds sticky events and state updates into the crate's sinks with their origin", async () => { + const { client, room, driver } = fullClient(); + const roomSink = sink(); + const stateSink = sink(); + driver.subscribeRoomEvents(roomSink as unknown as RoomEventSinkLike); + driver.subscribeStateUpdates(stateSink as unknown as StateUpdateSinkLike); + + const sticky = new MatrixEvent({ + type: "m.rtc.member", + sender: "@a:example.org", + event_id: "$s1", + room_id: ROOM_ID, + origin_server_ts: 1, + content: { slot_id: "m.call#ROOM" }, + }); + room.emit(RoomStickyEventsEvent.Update, [sticky], [], []); + await vi.waitFor(() => { + expect(roomSink.emit).toHaveBeenCalledTimes(1); + const [json, origin] = roomSink.emit.mock.calls[0] as unknown as [ + string, + FfiEventOrigin, + ]; + expect(JSON.parse(json)).toMatchObject({ + type: "m.rtc.member", + event_id: "$s1", + content: { slot_id: "m.call#ROOM" }, + }); + // not encrypted on this fake: the origin says so honestly + expect(FfiEventOrigin.Cleartext.instanceOf(origin)).toBe(true); + }); + + const slot = new MatrixEvent({ + type: "m.rtc.slot", + sender: "@admin:example.org", + event_id: "$slot", + room_id: ROOM_ID, + state_key: "m.call#ROOM", + origin_server_ts: 2, + content: { status: "open" }, + }); + client.emit(RoomStateEvent.Events, slot, null, null); + expect(stateSink.emit).toHaveBeenCalledTimes(1); + expect( + JSON.parse( + (stateSink.emit.mock.calls[0] as unknown as [string[]])[0][0], + ), + ).toMatchObject({ type: "m.rtc.slot", state_key: "m.call#ROOM" }); + + // another room's state is not ours + client.emit( + RoomStateEvent.Events, + new MatrixEvent({ ...slot.event, room_id: "!other:example.org" }), + null, + null, + ); + expect(stateSink.emit).toHaveBeenCalledTimes(1); + }); + + it("reports to-device messages with their Olm sender device and cross-signing verdict", async () => { + const { client, driver } = fullClient(); + const toDevice = sink(); + driver.subscribeToDeviceEvents(toDevice as unknown as ToDeviceSinkLike); + client.emit(ClientEvent.ReceivedToDeviceMessage, { + message: { + type: "m.rtc.encryption_key", + sender: "@a:example.org", + content: { member_id: "m-a" }, + }, + encryptionInfo: { + sender: "@a:example.org", + senderDevice: "ADEV", + senderCurve25519KeyBase64: "k", + }, + }); + await vi.waitFor(() => expect(toDevice.emit).toHaveBeenCalledTimes(1)); + const [type, sender, json, origin, crossSigned] = toDevice.emit.mock + .calls[0] as unknown as [ + string, + string, + string, + FfiEventOrigin, + boolean, + ]; + expect(type).toBe("m.rtc.encryption_key"); + expect(sender).toBe("@a:example.org"); + expect(JSON.parse(json)).toEqual({ member_id: "m-a" }); + expect(senderDeviceOf(origin)).toBe("ADEV"); + expect(crossSigned).toBe(true); + }); + + it("reports homeserver connectivity from the sync state", () => { + const { client, driver } = fullClient(); + expect(driver.isHomeserverConnected()).toBe(true); + const connectivity = sink(); + driver.subscribeConnectivity( + connectivity as unknown as ConnectivitySinkLike, + ); + client.getSyncState.mockReturnValue(SyncState.Error); + client.emit(ClientEvent.Sync, SyncState.Error, SyncState.Syncing); + expect(connectivity.emit).toHaveBeenCalledWith(false); + expect(driver.isHomeserverConnected()).toBe(false); + client.getSyncState.mockReturnValue(SyncState.Syncing); + client.emit(ClientEvent.Sync, SyncState.Syncing, SyncState.Error); + expect(connectivity.emit).toHaveBeenLastCalledWith(true); + }); + }); + + describe("over a RoomWidgetClient", () => { + it("listens for the legacy to-device event and reports the claimed device", async () => { + const { client, driver } = widgetClient(); + const toDevice = sink(); + driver.subscribeToDeviceEvents(toDevice as unknown as ToDeviceSinkLike); + const event = new MatrixEvent({ + type: "io.element.call.encryption_keys", + sender: "@a:example.org", + content: { member: { id: "m-a", claimed_device_id: "ADEV" } }, + }); + event.makeEncrypted("m.room.encrypted", {}, "", ""); + client.emit(ClientEvent.ToDeviceEvent, event); + await vi.waitFor(() => expect(toDevice.emit).toHaveBeenCalledTimes(1)); + const [, , , origin, crossSigned] = toDevice.emit.mock + .calls[0] as unknown as [ + string, + string, + string, + FfiEventOrigin, + boolean | undefined, + ]; + expect(senderDeviceOf(origin)).toBe("ADEV"); + expect(crossSigned).toBeUndefined(); + }); + + it("treats member events in an encrypted room as encrypted by the claimed device", async () => { + const { room, driver } = widgetClient(); + const roomSink = sink(); + driver.subscribeRoomEvents(roomSink as unknown as RoomEventSinkLike); + room.emit( + RoomStickyEventsEvent.Update, + [ + new MatrixEvent({ + type: "m.rtc.member", + sender: "@a:example.org", + event_id: "$s1", + room_id: ROOM_ID, + origin_server_ts: 1, + content: { slot_id: "m.call#ROOM", member: { device_id: "ADEV" } }, + }), + ], + [], + [], + ); + await vi.waitFor(() => expect(roomSink.emit).toHaveBeenCalledTimes(1)); + const origin = ( + roomSink.emit.mock.calls[0] as unknown as [string, FfiEventOrigin] + )[1]; + expect(senderDeviceOf(origin)).toBe("ADEV"); + }); + }); +}); + +function fullClient(): { + client: FakeClient; + room: FakeRoom; + driver: JsSdkRtcMatrixDriver; +} { + const client = fakeClient(false); + const room = fakeRoom(); + return { + client, + room, + driver: new JsSdkRtcMatrixDriver(asClient(client), asRoom(room)), + }; +} + +function widgetClient(): { + client: FakeClient; + room: FakeRoom; + driver: JsSdkRtcMatrixDriver; +} { + const client = fakeClient(true); + const room = fakeRoom(); + return { + client, + room, + driver: new JsSdkRtcMatrixDriver(asClient(client), asRoom(room)), + }; +} + +type SinkEmit = ReturnType boolean>>; + +function sink(): { emit: SinkEmit } { + return { emit: vi.fn<(...args: unknown[]) => boolean>(() => true) }; +} + +/** The sender device an origin carries, if it is an encrypted one. */ +function senderDeviceOf(origin: FfiEventOrigin): string | undefined { + return FfiEventOrigin.Encrypted.instanceOf(origin) + ? origin.inner.senderDeviceId + : undefined; +} diff --git a/src/driver/jsSdk/JsSdkRtcMatrixDriver.ts b/src/driver/jsSdk/JsSdkRtcMatrixDriver.ts new file mode 100644 index 000000000..c0c4da1e9 --- /dev/null +++ b/src/driver/jsSdk/JsSdkRtcMatrixDriver.ts @@ -0,0 +1,630 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +/** + * An {@link RtcMatrixDriver} over a matrix-js-sdk client: the crate's + * MatrixRTC seam, nothing else. The standalone app, the widget and the SDK + * build hand this to Element Call, and a host that already runs matrix-js-sdk + * can use it as it is. + * + * It serves two kinds of client. A full `MatrixClient` syncs, decrypts and + * has an access token of its own. A `RoomWidgetClient` is a shell over the + * widget API: it never sees ciphertext (its host decrypts), has no crypto + * backend and no token, only emits the legacy to-device event, and answers + * transport discovery over the widget API. Every place the two differ is + * marked "widget". + * + * Adapted from the crate's `web-test-app/src/jsSdkDriver.ts`. + */ + +import { + ClientEvent, + type IOpenIDToken, + type MatrixClient, + type MatrixEvent, + MatrixError, + type ReceivedToDeviceMessage, + type Room, + RoomEvent, + RoomStateEvent, + RoomStickyEventsEvent, + RoomWidgetClient, + SyncState, + UnsupportedDelayedEventsEndpointError, + UnsupportedStickyEventsEndpointError, + UpdateDelayedEventAction, + parseErrorResponse, +} from "matrix-js-sdk"; +import { type Transport } from "matrix-js-sdk/lib/matrixrtc"; +import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger"; + +import { + FfiEventOrigin, + RtcError, + type ConnectivitySinkLike, + type FfiLivekitToken, + type FfiLivekitTokenRequest, + type FfiRtcTransport, + type FfiSendEventResponse, + type FfiToDeviceDelivery, + type FfiToDeviceRecipient, + type RoomEventSinkLike, + type StateUpdateSinkLike, + type ToDeviceSinkLike, +} from "../../matrix-rtc-sdk"; +import { doNetworkOperationWithRetry } from "../../utils/matrix"; +import { type RtcMatrixDriver } from "../RtcMatrixDriver"; + +export interface JsSdkRtcMatrixDriverOptions { + logger?: Logger; +} + +export class JsSdkRtcMatrixDriver implements RtcMatrixDriver { + private readonly roomId: string; + private readonly logger: Logger; + /** Widget: the host decrypts for us and there is no crypto backend. */ + private readonly widget: boolean; + private readonly detachers: (() => void)[] = []; + /** curve25519 sender key → device id, per megolm-attributed sender. */ + private readonly senderDeviceCache = new Map(); + + public constructor( + private readonly client: MatrixClient, + private readonly room: Room, + options: JsSdkRtcMatrixDriverOptions = {}, + ) { + this.roomId = room.roomId; + this.widget = client instanceof RoomWidgetClient; + this.logger = (options.logger ?? rootLogger).getChild( + `[JsSdkRtcMatrixDriver ${room.roomId}]`, + ); + } + + /** Unhooks every client listener. The crate's sinks stop being fed. */ + public detach(): void { + for (const detach of this.detachers.splice(0)) detach(); + } + + // --- outbound -------------------------------------------------------------- + + public async sendStickyEvent( + roomId: string, + eventType: string, + contentJson: string, + durationMs: bigint, + ): Promise { + return guard(async () => { + const res = await this.client._unstable_sendStickyEvent( + roomId, + Number(durationMs), + null, + eventType as never, + JSON.parse(contentJson) as never, + ); + return { eventId: res.event_id, delayId: undefined }; + }); + } + + public async sendStateEvent( + roomId: string, + eventType: string, + stateKey: string, + contentJson: string, + ): Promise { + return guard(async () => { + const res = await this.client.sendStateEvent( + roomId, + eventType as never, + JSON.parse(contentJson) as never, + stateKey, + ); + return { eventId: res.event_id, delayId: undefined }; + }); + } + + public async sendDelayedEvent( + roomId: string, + eventType: string, + contentJson: string, + delayMs: bigint, + stickyDurationMs: bigint | undefined, + ): Promise { + return guard(async () => { + const content = JSON.parse(contentJson) as never; + const res = + stickyDurationMs === undefined + ? await this.client._unstable_sendDelayedEvent( + roomId, + { delay: Number(delayMs) }, + null, + eventType as never, + content, + ) + : await this.client._unstable_sendStickyDelayedEvent( + roomId, + Number(stickyDurationMs), + { delay: Number(delayMs) }, + null, + eventType as never, + content, + ); + return res.delay_id; + }); + } + + public async sendDelayedStateEvent( + roomId: string, + eventType: string, + stateKey: string, + contentJson: string, + delayMs: bigint, + ): Promise { + return guard(async () => { + const res = await this.client._unstable_sendDelayedStateEvent( + roomId, + { delay: Number(delayMs) }, + eventType as never, + JSON.parse(contentJson) as never, + stateKey, + ); + return res.delay_id; + }); + } + + public async restartDelayedEvent( + _roomId: string, + delayId: string, + ): Promise { + await guard(async () => + this.client._unstable_updateDelayedEvent( + delayId, + UpdateDelayedEventAction.Restart, + ), + ); + } + + public async cancelDelayedEvent( + _roomId: string, + delayId: string, + ): Promise { + await guard(async () => + this.client._unstable_updateDelayedEvent( + delayId, + UpdateDelayedEventAction.Cancel, + ), + ); + } + + /** + * MSC4195, the way Element Call has always done it: the MatrixRTC + * authorisation service takes over the delayed leave when asked for a token + * with `delay_id`, `delay_timeout` and the homeserver it should restart it + * at. The token in the answer is discarded. (Interim: plan item C5 moves + * the choice of route into the crate and leaves only primitives here.) + */ + public async delegateLivekitDelayedLeave( + roomId: string, + slotId: string, + memberJson: string, + delayId: string, + livekitServiceUrl: string | undefined, + delayMs: bigint, + ): Promise { + if (livekitServiceUrl === undefined) + throw new RtcError.Unsupported( + "A receive-only member has no transport to delegate to", + ); + await guard(async () => { + const member = JSON.parse(memberJson) as MemberClaims; + const delegation = { + delay_id: delayId, + delay_timeout: Number(delayMs), + delay_cs_api_url: this.client.baseUrl, + }; + await this.requestToken( + livekitServiceUrl, + roomId, + slotId, + member, + false, + delegation, + ); + }); + } + + public async sendToDevice( + recipients: FfiToDeviceRecipient[], + eventType: string, + contentJson: string, + ): Promise { + return guard(async () => { + // Olm-encrypted, per specific device — never `*`. On a widget client + // this asks the host to encrypt; the plain `sendToDevice` there would + // go out in clear. + await this.client.encryptAndSendToDevice( + eventType, + recipients, + JSON.parse(contentJson) as Record, + ); + return recipients.map((recipient) => ({ recipient, error: undefined })); + }); + } + + public async getRtcTransports(): Promise { + return guard(async () => { + // The homeserver endpoint (MSC4143), or the widget host's answer to + // the same question (MSC4515) — the client knows which. + const transports = await doNetworkOperationWithRetry(async () => + this.client._unstable_getRTCTransports(), + ); + return transports.map(({ type, ...properties }: Transport) => ({ + transportType: String(type), + propertiesJson: JSON.stringify(properties), + })); + }); + } + + public async getLivekitToken( + request: FfiLivekitTokenRequest, + ): Promise { + return guard(async () => { + const token = await this.requestToken( + request.url, + request.roomId, + request.slotId, + JSON.parse(request.memberJson) as MemberClaims, + request.legacySfuGet, + ); + return { jwt: token.jwt, url: token.url }; + }); + } + + private async requestToken( + serviceUrl: string, + roomId: string, + slotId: string, + member: MemberClaims, + legacySfuGet: boolean, + delegation: Record = {}, + ): Promise<{ jwt: string; url?: string }> { + let openIdToken: IOpenIDToken; + try { + openIdToken = await doNetworkOperationWithRetry(async () => + this.client.getOpenIdToken(), + ); + } catch (e) { + throw new RtcError.Http(`Could not get an OpenID token: ${String(e)}`); + } + const base = serviceUrl.replace(/\/$/, ""); + const [endpoint, body] = legacySfuGet + ? [ + `${base}/sfu/get`, + { + // The legacy endpoint derives the LiveKit room alias from the + // Matrix room id alone. + room: roomId, + openid_token: openIdToken, + device_id: member.claimed_device_id, + ...delegation, + }, + ] + : [ + `${base}/get_token`, + { + room_id: roomId, + slot_id: slotId, + openid_token: openIdToken, + member, + ...delegation, + }, + ]; + const response = await doNetworkOperationWithRetry(async () => + fetch(endpoint, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }), + ); + if (!response.ok) { + const text = await response.text(); + if (response.status === 404) + throw new RtcError.Unsupported(`${endpoint}: ${response.status}`); + throw parseErrorResponse(response, text); + } + return (await response.json()) as { jwt: string; url?: string }; + } + + // --- reads (the crate's session seed) --------------------------------------- + + public async readEvents( + eventType: string, + _stateKey: string | undefined, + limit: number, + ): Promise { + const out: string[] = []; + for (const event of this.room._unstable_getStickyEvents()) { + await this.client.decryptEventIfNeeded(event); + if (event.getType() !== eventType) continue; + out.push(JSON.stringify(rawEvent(event))); + if (out.length >= limit) break; + } + return out; + } + + public async readState( + eventType: string, + stateKey: string | undefined, + ): Promise { + const events = + stateKey === undefined + ? this.room.currentState.getStateEvents(eventType) + : [this.room.currentState.getStateEvents(eventType, stateKey)].filter( + (e): e is MatrixEvent => Boolean(e), + ); + return Promise.resolve( + events.map((event) => JSON.stringify(rawEvent(event))), + ); + } + + // --- inbound sinks ---------------------------------------------------------- + + public subscribeRoomEvents(sink: RoomEventSinkLike): void { + const emit = async (event: MatrixEvent): Promise => { + await this.client.decryptEventIfNeeded(event); + if (event.isDecryptionFailure()) { + this.logger.warn(`Event ${event.getId()} failed to decrypt; skipped`); + return; + } + if ( + !sink.emit( + JSON.stringify(rawEvent(event)), + await this.roomEventOrigin(event), + ) + ) + detach(); + }; + // Sticky events come from the room's sticky store: sync delivers them in + // the room's `msc4354_sticky` section, our own included. + const onSticky = ( + added: MatrixEvent[], + updated: { current: MatrixEvent }[], + ): void => { + for (const event of [...added, ...updated.map((u) => u.current)]) + void emit(event); + }; + // Everything else (state events in the timeline) comes from the timeline; + // local echoes and sticky events are skipped there. + const onTimeline = ( + event: MatrixEvent, + room: Room | undefined, + toStartOfTimeline: boolean | undefined, + ): void => { + if (room?.roomId !== this.roomId || toStartOfTimeline) return; + if (event.status !== null || event.unstableStickyInfo !== undefined) + return; + void emit(event); + }; + const detach = (): void => { + this.room.off(RoomStickyEventsEvent.Update, onSticky); + this.client.off(RoomEvent.Timeline, onTimeline); + }; + this.room.on(RoomStickyEventsEvent.Update, onSticky); + this.client.on(RoomEvent.Timeline, onTimeline); + this.detachers.push(detach); + } + + public subscribeStateUpdates(sink: StateUpdateSinkLike): void { + // Client-level listener: the room-level re-emit is unreliable under + // MSC4222 `state_after` churn. + const onState = (event: MatrixEvent): void => { + if (event.getRoomId() !== this.roomId) return; + if (!sink.emit([JSON.stringify(rawEvent(event))])) detach(); + }; + const detach = (): void => { + this.client.off(RoomStateEvent.Events, onState); + }; + this.client.on(RoomStateEvent.Events, onState); + this.detachers.push(detach); + } + + public subscribeToDeviceEvents(sink: ToDeviceSinkLike): void { + if (this.widget) { + // Widget: the legacy event is the only one the widget client emits. + // The host decrypted the message; it says whether it was encrypted but + // not by which device, so the device is the one the content claims. + const onToDevice = (event: MatrixEvent): void => { + const content = event.getContent() as KeyMessageContent; + const origin = event.isEncrypted() + ? new FfiEventOrigin.Encrypted({ + senderDeviceId: claimedKeyDevice(content), + }) + : new FfiEventOrigin.Cleartext(); + if ( + !sink.emit( + event.getType(), + event.getSender() ?? "", + JSON.stringify(content), + origin, + undefined, + ) + ) + detach(); + }; + const detach = (): void => { + this.client.off(ClientEvent.ToDeviceEvent, onToDevice); + }; + this.client.on(ClientEvent.ToDeviceEvent, onToDevice); + this.detachers.push(detach); + return; + } + const onToDevice = (received: ReceivedToDeviceMessage): void => + void handleToDevice(received); + const handleToDevice = async ({ + message, + encryptionInfo, + }: ReceivedToDeviceMessage): Promise => { + const origin = encryptionInfo + ? new FfiEventOrigin.Encrypted({ + senderDeviceId: encryptionInfo.senderDevice, + }) + : new FfiEventOrigin.Cleartext(); + // MSC4153: is the sending device cross-signed by its owner? + let crossSigned: boolean | undefined; + const crypto = this.client.getCrypto(); + if (crypto && encryptionInfo?.senderDevice) { + const status = await crypto.getDeviceVerificationStatus( + encryptionInfo.sender, + encryptionInfo.senderDevice, + ); + crossSigned = status?.signedByOwner ?? false; + } + if ( + !sink.emit( + message.type, + message.sender, + JSON.stringify(message.content ?? {}), + origin, + crossSigned, + ) + ) + detach(); + }; + const detach = (): void => { + this.client.off(ClientEvent.ReceivedToDeviceMessage, onToDevice); + }; + this.client.on(ClientEvent.ReceivedToDeviceMessage, onToDevice); + this.detachers.push(detach); + } + + // --- connectivity ------------------------------------------------------------ + + public isHomeserverConnected(): boolean { + // Widget: the widget client reports Syncing once it has seen an event + // and never anything else, so on a widget this is always true. + return this.client.getSyncState() === SyncState.Syncing; + } + + public subscribeConnectivity(sink: ConnectivitySinkLike): void { + const onSync = (): void => { + if (!sink.emit(this.isHomeserverConnected())) detach(); + }; + const detach = (): void => { + this.client.off(ClientEvent.Sync, onSync); + }; + this.client.on(ClientEvent.Sync, onSync); + this.detachers.push(detach); + } + + private async roomEventOrigin(event: MatrixEvent): Promise { + if (this.widget) { + // Widget: events arrive decrypted with no metadata. In an encrypted + // room they were encrypted, by the device the content claims — the + // same trust matrix-js-sdk's own session extends. + if (event.isState() || !this.room.hasEncryptionStateEvent()) + return new FfiEventOrigin.Cleartext(); + return new FfiEventOrigin.Encrypted({ + senderDeviceId: claimedMemberDevice(event.getContent()), + }); + } + if (!event.isEncrypted()) return new FfiEventOrigin.Cleartext(); + return new FfiEventOrigin.Encrypted({ + senderDeviceId: await this.senderDeviceOf(event), + }); + } + + /** The device that megolm-encrypted `event` (sender key → device list). */ + private async senderDeviceOf( + event: MatrixEvent, + ): Promise { + const senderKey = event.getSenderKey(); + const sender = event.getSender(); + const crypto = this.client.getCrypto(); + if (!senderKey || !sender || !crypto) return undefined; + const cached = this.senderDeviceCache.get(senderKey); + if (cached) return cached; + const devices = await crypto.getUserDeviceInfo([sender], true); + for (const device of devices.get(sender)?.values() ?? []) { + if (device.getIdentityKey() === senderKey) { + this.senderDeviceCache.set(senderKey, device.deviceId); + return device.deviceId; + } + } + return undefined; + } +} + +/** The full (decrypted) event object the crate's dispatch reads. */ +function rawEvent(event: MatrixEvent): Record { + return { + ...(event.event as Record), + type: event.getType(), + content: event.getContent(), + sender: event.getSender(), + event_id: event.getId(), + room_id: event.getRoomId(), + origin_server_ts: event.getTs(), + state_key: event.getStateKey(), + }; +} + +/** MSC4195 member claims, as the crate serialises them. */ +interface MemberClaims { + id: string; + claimed_user_id: string; + claimed_device_id: string; +} + +/** The device a media key message claims to come from, in either dialect. */ +interface KeyMessageContent { + member?: { claimed_device_id?: string }; + device_id?: string; +} + +function claimedKeyDevice(content: KeyMessageContent): string | undefined { + return content.member?.claimed_device_id ?? content.device_id; +} + +/** The device a 2025-dialect membership claims (`member.device_id`). */ +function claimedMemberDevice( + content: Record, +): string | undefined { + const member = content.member as { device_id?: unknown } | undefined; + return typeof member?.device_id === "string" ? member.device_id : undefined; +} + +/** Map js-sdk / HTTP failures onto the error the crate reasons about. */ +function toRtcError(error: unknown): Error { + if (RtcError.instanceOf(error)) return error; + if ( + error instanceof UnsupportedDelayedEventsEndpointError || + error instanceof UnsupportedStickyEventsEndpointError + ) + return new RtcError.Unsupported(String(error)); + if (error instanceof MatrixError) { + if (error.errcode === "M_LIMIT_EXCEEDED") + return new RtcError.RateLimited({ + retryAfterMs: + typeof error.data.retry_after_ms === "number" + ? BigInt(error.data.retry_after_ms) + : undefined, + }); + if (error.httpStatus === 404 || error.errcode === "M_UNRECOGNIZED") + return new RtcError.Unsupported(String(error)); + if (error.httpStatus === 403 || error.errcode === "M_FORBIDDEN") + return new RtcError.Rejected(String(error)); + return new RtcError.Http(String(error)); + } + return new RtcError.Driver(String(error)); +} + +async function guard(f: () => Promise): Promise { + try { + return await f(); + } catch (error) { + throw toRtcError(error); + } +} diff --git a/src/driver/jsSdk/jsSdkTestFakes.ts b/src/driver/jsSdk/jsSdkTestFakes.ts new file mode 100644 index 000000000..4c61a2c0d --- /dev/null +++ b/src/driver/jsSdk/jsSdkTestFakes.ts @@ -0,0 +1,150 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +// Fakes shared by the js-sdk driver tests. The drivers tell the two client +// kinds apart by prototype, so each fake is created on the real prototype +// and given only the members it needs; EventEmitter sits at the bottom of +// that chain, so `on`/`emit` work. + +import { EventEmitter } from "events"; +import { + MatrixClient, + type Room, + RoomWidgetClient, + SyncState, + User, +} from "matrix-js-sdk"; +import { vi } from "vitest"; + +export const ROOM_ID = "!room:example.org"; +export const ME = "@me:example.org"; +export const MY_DEVICE = "MYDEV"; +export const LK = "https://lk.example.org"; + +export const openIdToken = { + access_token: "openid", + expires_in: 3600, + matrix_server_name: "example.org", + token_type: "Bearer", +}; + +type Fn = ReturnType; + +export interface FakeClient extends EventEmitter { + _unstable_sendStickyEvent: Fn; + _unstable_sendDelayedEvent: Fn; + _unstable_sendStickyDelayedEvent: Fn; + _unstable_sendDelayedStateEvent: Fn; + _unstable_updateDelayedEvent: Fn; + _unstable_getRTCTransports: Fn; + sendStateEvent: Fn; + encryptAndSendToDevice: Fn; + sendEvent: Fn; + redactEvent: Fn; + getSyncState: Fn; + getUser: (userId: string) => User | null; +} + +export interface FakeRoom extends EventEmitter { + name: string; +} + +function clientMembers(widget: boolean): Record { + const user = new User(ME); + user.rawDisplayName = "Me"; + user.avatarUrl = "mxc://example.org/me"; + const verification = { signedByOwner: true }; + return { + baseUrl: "https://hs.example.org", + getUserId: () => ME, + getDeviceId: () => MY_DEVICE, + getAccessToken: () => (widget ? null : "token"), + getCrypto: () => + widget + ? undefined + : { + getVersion: () => "fake 1.0", + getDeviceVerificationStatus: async () => + Promise.resolve(verification), + getUserDeviceInfo: async () => Promise.resolve(new Map()), + }, + getSyncState: vi.fn(() => SyncState.Syncing), + getUser: () => user, + getOpenIdToken: async () => Promise.resolve(openIdToken), + decryptEventIfNeeded: async () => Promise.resolve(), + doesServerSupportUnstableFeature: async () => Promise.resolve(true), + mxcUrlToHttp: (mxc: string) => `https://hs.example.org/media/${mxc}`, + _unstable_sendStickyEvent: vi.fn(async () => + Promise.resolve({ event_id: "$sticky" }), + ), + _unstable_sendDelayedEvent: vi.fn(async () => + Promise.resolve({ delay_id: "delay-plain" }), + ), + _unstable_sendStickyDelayedEvent: vi.fn(async () => + Promise.resolve({ delay_id: "delay-sticky" }), + ), + _unstable_sendDelayedStateEvent: vi.fn(async () => + Promise.resolve({ delay_id: "delay-state" }), + ), + _unstable_updateDelayedEvent: vi.fn(async () => Promise.resolve({})), + _unstable_getRTCTransports: vi.fn(async () => Promise.resolve([])), + sendStateEvent: vi.fn(async () => Promise.resolve({ event_id: "$state" })), + encryptAndSendToDevice: vi.fn(async () => Promise.resolve()), + sendEvent: vi.fn(async () => Promise.resolve({ event_id: "$sent" })), + redactEvent: vi.fn(async () => Promise.resolve({ event_id: "$redaction" })), + }; +} + +export function fakeRoom(): FakeRoom { + const room = new EventEmitter() as FakeRoom; + const alice = { + userId: "@a:example.org", + rawDisplayName: "Alice", + getMxcAvatarUrl: () => "mxc://example.org/alice", + }; + const bob = { + userId: "@b:example.org", + rawDisplayName: undefined, + getMxcAvatarUrl: () => undefined, + }; + Object.assign(room, { + roomId: ROOM_ID, + name: "Standup", + getCanonicalAlias: () => "#standup:example.org", + getMxcAvatarUrl: () => "mxc://example.org/room", + hasEncryptionStateEvent: () => true, + currentState: { + getJoinRule: () => "public", + getStateEvents: () => [], + maySendStateEvent: () => true, + }, + getMembersWithMembership: (membership: string) => + membership === "join" ? [alice] : [bob], + _unstable_getStickyEvents: () => [], + relations: { getChildEventsForEvent: () => undefined }, + }); + return room; +} + +export function fakeClient(widget: boolean): FakeClient { + const client = Object.create( + widget ? RoomWidgetClient.prototype : MatrixClient.prototype, + ) as FakeClient; + Object.assign(client, clientMembers(widget)); + return client; +} + +export const asClient = (client: FakeClient): MatrixClient => + client as unknown as MatrixClient; +export const asRoom = (room: FakeRoom): Room => room as unknown as Room; + +export function jsonResponse(body: unknown): Response { + return new Response(JSON.stringify(body), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); +} diff --git a/src/driver/observe.ts b/src/driver/observe.ts new file mode 100644 index 000000000..55976d516 --- /dev/null +++ b/src/driver/observe.ts @@ -0,0 +1,29 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { Observable } from "rxjs"; + +import { type Behavior } from "../state/Behavior"; +import { type ObservableScope } from "../state/ObservableScope"; +import { type Unsubscribe } from "./ElementCallMatrixClientDriver"; + +/** + * Turns one of a driver's `get` / `subscribe` pairs into a behavior owned by + * `scope`: the current value now, every change until the scope ends. + */ +export function observeDriver( + scope: ObservableScope, + get: () => T, + subscribe: (listener: (value: T) => void) => Unsubscribe, +): Behavior { + return scope.behavior( + new Observable((subscriber) => + subscribe((value) => subscriber.next(value)), + ), + get(), + ); +} diff --git a/src/matrix-rtc-sdk/generated/VERSION b/src/matrix-rtc-sdk/generated/VERSION new file mode 100644 index 000000000..692c326aa --- /dev/null +++ b/src/matrix-rtc-sdk/generated/VERSION @@ -0,0 +1,2 @@ +matrix-rtc (MatrixSdkArchitectureDraft) a095ba7-dirty +built 2026-09-15T16:54:50Z by scripts/sync-matrix-rtc-sdk.sh diff --git a/src/matrix-rtc-sdk/generated/matrix_rtc-ffi.ts b/src/matrix-rtc-sdk/generated/matrix_rtc-ffi.ts new file mode 100644 index 000000000..f71af07bf --- /dev/null +++ b/src/matrix-rtc-sdk/generated/matrix_rtc-ffi.ts @@ -0,0 +1,103 @@ +// This file was autogenerated by some hot garbage in the `uniffi-bindgen-react-native` crate. +// Trust me, you don't want to mess with it! + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +// Structs and function types for calling back into Typescript from Rust. +export type UniffiRustFutureContinuationCallback = (data: bigint, pollResult: number) => void; +export type UniffiForeignFutureDroppedCallback = (handle: bigint) => void; +export type UniffiForeignFutureDroppedCallbackStruct = { + handle: bigint; + free: UniffiForeignFutureDroppedCallback; +}; +type UniffiCallbackInterfaceMatrixRtcConnectionsListenerMethod0 = (uniffiHandle: bigint, connections: Uint8Array) => UniffiResult; +type UniffiCallbackInterfaceCloneMatrixRtcConnectionsListener = (handle: bigint) => UniffiResult; +type UniffiCallbackInterfaceFreeMatrixRtcConnectionsListener = (handle: bigint) => void; +export type UniffiVTableCallbackInterfaceMatrixRtcConnectionsListener = { + uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcConnectionsListener; + uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcConnectionsListener; + on_connections_change: UniffiCallbackInterfaceMatrixRtcConnectionsListenerMethod0; +}; +type UniffiCallbackInterfaceMatrixRtcKeyMapListenerMethod0 = (uniffiHandle: bigint, keyMap: Uint8Array, change: Uint8Array) => UniffiResult; +type UniffiCallbackInterfaceCloneMatrixRtcKeyMapListener = (handle: bigint) => UniffiResult; +type UniffiCallbackInterfaceFreeMatrixRtcKeyMapListener = (handle: bigint) => void; +export type UniffiVTableCallbackInterfaceMatrixRtcKeyMapListener = { + uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcKeyMapListener; + uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcKeyMapListener; + on_key_map_change: UniffiCallbackInterfaceMatrixRtcKeyMapListenerMethod0; +}; +type UniffiCallbackInterfaceMatrixRtcKeyRejectedListenerMethod0 = (uniffiHandle: bigint, memberId: Uint8Array, reason: Uint8Array) => UniffiResult; +type UniffiCallbackInterfaceCloneMatrixRtcKeyRejectedListener = (handle: bigint) => UniffiResult; +type UniffiCallbackInterfaceFreeMatrixRtcKeyRejectedListener = (handle: bigint) => void; +export type UniffiVTableCallbackInterfaceMatrixRtcKeyRejectedListener = { + uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcKeyRejectedListener; + uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcKeyRejectedListener; + on_key_rejected: UniffiCallbackInterfaceMatrixRtcKeyRejectedListenerMethod0; +}; +export type UniffiForeignFutureResultRustBuffer = { + return_value: Uint8Array; + call_status: UniffiRustCallStatus; +}; +export type UniffiForeignFutureCompleterustBuffer = (callbackData: bigint, result: UniffiForeignFutureResultRustBuffer) => void; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod0 = (uniffiHandle: bigint, roomId: Uint8Array, eventType: Uint8Array, contentJson: Uint8Array, durationMs: bigint, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod1 = (uniffiHandle: bigint, roomId: Uint8Array, eventType: Uint8Array, stateKey: Uint8Array, contentJson: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod2 = (uniffiHandle: bigint, roomId: Uint8Array, eventType: Uint8Array, contentJson: Uint8Array, delayMs: bigint, stickyDurationMs: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod3 = (uniffiHandle: bigint, roomId: Uint8Array, eventType: Uint8Array, stateKey: Uint8Array, contentJson: Uint8Array, delayMs: bigint, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +export type UniffiForeignFutureResultVoid = { + call_status: UniffiRustCallStatus; +}; +export type UniffiForeignFutureCompletevoid = (callbackData: bigint, result: UniffiForeignFutureResultVoid) => void; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod4 = (uniffiHandle: bigint, roomId: Uint8Array, delayId: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompletevoid, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod5 = (uniffiHandle: bigint, roomId: Uint8Array, delayId: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompletevoid, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod6 = (uniffiHandle: bigint, roomId: Uint8Array, slotId: Uint8Array, memberJson: Uint8Array, delayId: Uint8Array, livekitServiceUrl: Uint8Array, delayMs: bigint, uniffiFutureCallback: UniffiForeignFutureCompletevoid, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod7 = (uniffiHandle: bigint, recipients: Uint8Array, eventType: Uint8Array, contentJson: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod8 = (uniffiHandle: bigint, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod9 = (uniffiHandle: bigint, request: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod10 = (uniffiHandle: bigint, eventType: Uint8Array, stateKey: Uint8Array, limit: number, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod11 = (uniffiHandle: bigint, eventType: Uint8Array, stateKey: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod12 = (uniffiHandle: bigint, sink: bigint) => UniffiResult; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod13 = (uniffiHandle: bigint, sink: bigint) => UniffiResult; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod14 = (uniffiHandle: bigint, sink: bigint) => UniffiResult; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod15 = (uniffiHandle: bigint) => number; +type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod16 = (uniffiHandle: bigint, sink: bigint) => UniffiResult; +type UniffiCallbackInterfaceCloneMatrixRtcMatrixDriverCallback = (handle: bigint) => UniffiResult; +type UniffiCallbackInterfaceFreeMatrixRtcMatrixDriverCallback = (handle: bigint) => void; +export type UniffiVTableCallbackInterfaceMatrixRtcMatrixDriverCallback = { + uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcMatrixDriverCallback; + uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcMatrixDriverCallback; + send_sticky_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod0; + send_state_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod1; + send_delayed_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod2; + send_delayed_state_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod3; + restart_delayed_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod4; + cancel_delayed_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod5; + delegate_livekit_delayed_leave: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod6; + send_to_device: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod7; + get_rtc_transports: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod8; + get_livekit_token: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod9; + read_events: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod10; + read_state: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod11; + subscribe_room_events: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod12; + subscribe_to_device_events: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod13; + subscribe_state_updates: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod14; + is_homeserver_connected: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod15; + subscribe_connectivity: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod16; +}; +type UniffiCallbackInterfaceMatrixRtcMembershipsListenerMethod0 = (uniffiHandle: bigint, memberships: Uint8Array) => UniffiResult; +type UniffiCallbackInterfaceCloneMatrixRtcMembershipsListener = (handle: bigint) => UniffiResult; +type UniffiCallbackInterfaceFreeMatrixRtcMembershipsListener = (handle: bigint) => void; +export type UniffiVTableCallbackInterfaceMatrixRtcMembershipsListener = { + uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcMembershipsListener; + uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcMembershipsListener; + on_memberships_change: UniffiCallbackInterfaceMatrixRtcMembershipsListenerMethod0; +}; +type UniffiCallbackInterfaceMatrixRtcStatusListenerMethod0 = (uniffiHandle: bigint, status: Uint8Array) => UniffiResult; +type UniffiCallbackInterfaceCloneMatrixRtcStatusListener = (handle: bigint) => UniffiResult; +type UniffiCallbackInterfaceFreeMatrixRtcStatusListener = (handle: bigint) => void; +export type UniffiVTableCallbackInterfaceMatrixRtcStatusListener = { + uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcStatusListener; + uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcStatusListener; + on_status_change: UniffiCallbackInterfaceMatrixRtcStatusListenerMethod0; +}; \ No newline at end of file diff --git a/src/matrix-rtc-sdk/generated/matrix_rtc.ts b/src/matrix-rtc-sdk/generated/matrix_rtc.ts new file mode 100644 index 000000000..7098b51e2 --- /dev/null +++ b/src/matrix-rtc-sdk/generated/matrix_rtc.ts @@ -0,0 +1,8716 @@ +// This file was autogenerated by some hot garbage in the `uniffi-bindgen-react-native` crate. +// Trust me, you don't want to mess with it! +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck +import * as wasmBundle from "./wasm-bindgen/index.js"; +import { type UniffiRustFutureContinuationCallback, type UniffiForeignFutureDroppedCallback, type UniffiForeignFutureDroppedCallbackStruct, type UniffiVTableCallbackInterfaceMatrixRtcConnectionsListener, type UniffiVTableCallbackInterfaceMatrixRtcKeyMapListener, type UniffiVTableCallbackInterfaceMatrixRtcKeyRejectedListener, type UniffiForeignFutureResultRustBuffer, type UniffiForeignFutureCompleterustBuffer, type UniffiForeignFutureResultVoid, type UniffiForeignFutureCompletevoid, type UniffiVTableCallbackInterfaceMatrixRtcMatrixDriverCallback, type UniffiVTableCallbackInterfaceMatrixRtcMembershipsListener, type UniffiVTableCallbackInterfaceMatrixRtcStatusListener, +} from "./matrix_rtc-ffi"; +import { type FfiConverter, type UniffiByteArray, type UniffiGcObject, type UniffiHandle, type UniffiObjectFactory, type UniffiReferenceHolder, type UniffiRustCallStatus, AbstractFfiConverterByteArray, Cursor, FfiConverterArray, FfiConverterArrayBuffer, FfiConverterBool, FfiConverterObject, FfiConverterObjectWithCallbacks, FfiConverterOptional, FfiConverterUInt32, FfiConverterUInt64, FfiConverterUInt8, RustBuffer, UniffiAbstractObject, UniffiEnum, UniffiError, UniffiInternalError, UniffiResult, UniffiRustCaller, destructorGuardSymbol, pointerLiteralSymbol, uniffiCreateFfiConverterString, uniffiCreateRecord, uniffiRustCallAsync, uniffiTraitInterfaceCall, uniffiTraitInterfaceCallAsyncWithError, uniffiTypeNameSymbol, variantOrdinalSymbol, +} from "@ubjs/core"; +// wasm1: wrap the wasm-bindgen namespace once so the codegen call sites can +// uniformly reference `nativeModule().rustbuffer_alloc` / `.rustbuffer_free`. +// For wasm1, RustBuffers are plain JS Uint8Arrays — no Rust-side allocation +// is involved, so alloc just hands back a fresh view and free is a no-op. +const _nativeModule = Object.assign({}, wasmBundle, { + rustbuffer_alloc: (n: number): Uint8Array => new Uint8Array(n), + rustbuffer_free: (_: Uint8Array): void => {}, +}); +const nativeModule = () => _nativeModule; +const uniffiCaller = new UniffiRustCaller(() => new wasmBundle.RustCallStatus()); + +const uniffiIsDebug = + // @ts-ignore -- The process global might not be defined + typeof process !== "object" || + // @ts-ignore -- The process global might not be defined + process?.env?.NODE_ENV !== "production" || + false; + +// Public interface members begin here. + +/** + * Static session computation for room-list / header info: values, not + * subscriptions — call it on every room update and populate room_info from + * the snapshot fields. Takes all relevant events in one list (sticky and + * state, many rooms at once — the dispatch groups by room and slot). + * Origins are unknown here, so origin-dependent conditions stay + * unenforced — fine for room-info purposes. + */ +export function computeSessionsFromEvents(eventsJson: Array, compat: FfiElementCallCompat): Array /*throws*/ { + const __rb: Uint8Array = uniffiCaller.rustCallWithError( + /*liftError:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError), + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_func_compute_sessions_from_events( + FfiConverterSequenceString.lower(eventsJson, nativeModule().rustbuffer_alloc), + FfiConverterTypeFfiElementCallCompat.lower(compat, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterSequenceTypeFfiSessionSnapshot.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + +/** + * The severity of one impairment, so a host can sort or filter without + * re-deriving the table. (`impairments` already arrives sorted, most severe + * first.) + */ +export function impairmentSeverity(impairment: FfiImpairment): FfiSeverity { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_func_impairment_severity( + FfiConverterTypeFfiImpairment.lower(impairment, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterTypeFfiSeverity.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + +const stringConverter = (() => { + const encoder = new TextEncoder(); + const decoder = new TextDecoder(); + return { + stringToBytes: (s: string) => encoder.encode(s), + bytesToString: (ab: UniffiByteArray) => decoder.decode(ab), + stringByteLength: (s: string) => encoder.encode(s).byteLength, + writeStringIntoBuffer: (s: string, buf: any, offset: number): number => { + const view = new Uint8Array( + buf.arrayBuffer, + offset, + buf.arrayBuffer.byteLength - offset, + ); + return encoder.encodeInto(s, view).written; + }, + readStringFromBuffer: (buf: any, offset: number, length: number): string => + decoder.decode(new Uint8Array(buf.arrayBuffer, offset, length)), + }; +})(); +const FfiConverterString = uniffiCreateFfiConverterString(stringConverter); + +export type FfiConnectionData = { + /** + * The connection key (`livekit_service_url`) `FfiMembership::connections` + * refers to. + */ + serviceUrl: string, + /** + * The SFU websocket URL to connect to. + */ + wsUrl: string, + jwtToken: string, + /** + * From the JWT's `exp`; `None` when it carries none. A failed re-mint + * keeps the old token (a host may still be connected on it), so check + * this rather than discovering staleness through a failed connect. + */ + expiresAtTs?: bigint +} + +/** + * Generated factory for {@link FfiConnectionData} record objects. + */ +export const FfiConnectionData = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiConnectionData = (() => { + type TypeName = FfiConnectionData; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + serviceUrl: FfiConverterString.readFromCursor(c), + wsUrl: FfiConverterString.readFromCursor(c), + jwtToken: FfiConverterString.readFromCursor(c), + expiresAtTs: FfiConverterOptionalUInt64.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.serviceUrl, c); + FfiConverterString.writeIntoCursor(value.wsUrl, c); + FfiConverterString.writeIntoCursor(value.jwtToken, c); + FfiConverterOptionalUInt64.writeIntoCursor(value.expiresAtTs, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.serviceUrl) + + FfiConverterString.allocationSize(value.wsUrl) + + FfiConverterString.allocationSize(value.jwtToken) + + FfiConverterOptionalUInt64.allocationSize(value.expiresAtTs); + + } + }; + return new FFIConverter(); +})(); + +export enum FfiConnectionProblemKind { + /** + * Wanted, never minted — absent from `connections()` entirely. + */ + NoToken, + /** + * Present in `connections()` but its JWT is past `exp`. + */ + TokenExpired +} + +const FfiConverterTypeFfiConnectionProblemKind = (() => { + type TypeName = FfiConnectionProblemKind; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiConnectionProblemKind.NoToken; + case 2: return FfiConnectionProblemKind.TokenExpired; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiConnectionProblemKind.NoToken: return c.writeI32(1); + case FfiConnectionProblemKind.TokenExpired: return c.writeI32(2); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + +/** + * A connection the session needs that the host cannot currently use. + */ +export type FfiConnectionProblem = { + serviceUrl: string, + /** + * Members whose media is unavailable because of it. + */ + memberIds: Array, + kind: FfiConnectionProblemKind, + lastError: string, + /** + * When the next mint is due; `0` = at the next beat. + */ + retryAtTs: bigint +} + +/** + * Generated factory for {@link FfiConnectionProblem} record objects. + */ +export const FfiConnectionProblem = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiConnectionProblem = (() => { + type TypeName = FfiConnectionProblem; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + serviceUrl: FfiConverterString.readFromCursor(c), + memberIds: FfiConverterSequenceString.readFromCursor(c), + kind: FfiConverterTypeFfiConnectionProblemKind.readFromCursor(c), + lastError: FfiConverterString.readFromCursor(c), + retryAtTs: FfiConverterUInt64.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.serviceUrl, c); + FfiConverterSequenceString.writeIntoCursor(value.memberIds, c); + FfiConverterTypeFfiConnectionProblemKind.writeIntoCursor(value.kind, c); + FfiConverterString.writeIntoCursor(value.lastError, c); + FfiConverterUInt64.writeIntoCursor(value.retryAtTs, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.serviceUrl) + + FfiConverterSequenceString.allocationSize(value.memberIds) + + FfiConverterTypeFfiConnectionProblemKind.allocationSize(value.kind) + + FfiConverterString.allocationSize(value.lastError) + + FfiConverterUInt64.allocationSize(value.retryAtTs); + + } + }; + return new FFIConverter(); +})(); + +export enum FfiDeviceAttribution { + Verified, + Claimed, + Unknown +} + +const FfiConverterTypeFfiDeviceAttribution = (() => { + type TypeName = FfiDeviceAttribution; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiDeviceAttribution.Verified; + case 2: return FfiDeviceAttribution.Claimed; + case 3: return FfiDeviceAttribution.Unknown; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiDeviceAttribution.Verified: return c.writeI32(1); + case FfiDeviceAttribution.Claimed: return c.writeI32(2); + case FfiDeviceAttribution.Unknown: return c.writeI32(3); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + +export type FfiRtcTransport = { + transportType: string, + /** + * Type-specific fields as a JSON string (LiveKit: `livekit_service_url`). + */ + propertiesJson: string +} + +/** + * Generated factory for {@link FfiRtcTransport} record objects. + */ +export const FfiRtcTransport = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiRtcTransport = (() => { + type TypeName = FfiRtcTransport; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + transportType: FfiConverterString.readFromCursor(c), + propertiesJson: FfiConverterString.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.transportType, c); + FfiConverterString.writeIntoCursor(value.propertiesJson, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.transportType) + + FfiConverterString.allocationSize(value.propertiesJson); + + } + }; + return new FFIConverter(); +})(); + +export type FfiMember = { + memberId: string, + userId: string, + deviceId?: string, + deviceAttribution: FfiDeviceAttribution, + /** + * `origin_server_ts` of the event that started this participation, where + * the dialect needs it to tell joins apart (MSC3401 compat). + */ + membershipTs?: bigint, + /** + * From the room's `m.room.member` state for this user, kept current by + * the session: a rename shows up as a memberships change. + */ + displayName?: string, + avatarUrl?: string, + /** + * The membership event this entry was projected from — the *current* + * one, so it changes on every re-send. Relate application events + * (reactions, hand raises) to it. `None` for a member without an event + * yet (our own entry before the echo). + */ + eventId?: string, + intent?: string, + applicationType?: string, + /** + * The transports this member publishes on. + */ + publishedTransports: Array, + canSubscribe: Array +} + +/** + * Generated factory for {@link FfiMember} record objects. + */ +export const FfiMember = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiMember = (() => { + type TypeName = FfiMember; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + memberId: FfiConverterString.readFromCursor(c), + userId: FfiConverterString.readFromCursor(c), + deviceId: FfiConverterOptionalString.readFromCursor(c), + deviceAttribution: FfiConverterTypeFfiDeviceAttribution.readFromCursor(c), + membershipTs: FfiConverterOptionalUInt64.readFromCursor(c), + displayName: FfiConverterOptionalString.readFromCursor(c), + avatarUrl: FfiConverterOptionalString.readFromCursor(c), + eventId: FfiConverterOptionalString.readFromCursor(c), + intent: FfiConverterOptionalString.readFromCursor(c), + applicationType: FfiConverterOptionalString.readFromCursor(c), + publishedTransports: FfiConverterSequenceTypeFfiRtcTransport.readFromCursor(c), + canSubscribe: FfiConverterSequenceString.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.memberId, c); + FfiConverterString.writeIntoCursor(value.userId, c); + FfiConverterOptionalString.writeIntoCursor(value.deviceId, c); + FfiConverterTypeFfiDeviceAttribution.writeIntoCursor(value.deviceAttribution, c); + FfiConverterOptionalUInt64.writeIntoCursor(value.membershipTs, c); + FfiConverterOptionalString.writeIntoCursor(value.displayName, c); + FfiConverterOptionalString.writeIntoCursor(value.avatarUrl, c); + FfiConverterOptionalString.writeIntoCursor(value.eventId, c); + FfiConverterOptionalString.writeIntoCursor(value.intent, c); + FfiConverterOptionalString.writeIntoCursor(value.applicationType, c); + FfiConverterSequenceTypeFfiRtcTransport.writeIntoCursor(value.publishedTransports, c); + FfiConverterSequenceString.writeIntoCursor(value.canSubscribe, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.memberId) + + FfiConverterString.allocationSize(value.userId) + + FfiConverterOptionalString.allocationSize(value.deviceId) + + FfiConverterTypeFfiDeviceAttribution.allocationSize(value.deviceAttribution) + + FfiConverterOptionalUInt64.allocationSize(value.membershipTs) + + FfiConverterOptionalString.allocationSize(value.displayName) + + FfiConverterOptionalString.allocationSize(value.avatarUrl) + + FfiConverterOptionalString.allocationSize(value.eventId) + + FfiConverterOptionalString.allocationSize(value.intent) + + FfiConverterOptionalString.allocationSize(value.applicationType) + + FfiConverterSequenceTypeFfiRtcTransport.allocationSize(value.publishedTransports) + + FfiConverterSequenceString.allocationSize(value.canSubscribe); + + } + }; + return new FFIConverter(); +})(); + +export type FfiConnectionWithMembers = { + connection: FfiConnectionData, + members: Array +} + +/** + * Generated factory for {@link FfiConnectionWithMembers} record objects. + */ +export const FfiConnectionWithMembers = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiConnectionWithMembers = (() => { + type TypeName = FfiConnectionWithMembers; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + connection: FfiConverterTypeFfiConnectionData.readFromCursor(c), + members: FfiConverterSequenceTypeFfiMember.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterTypeFfiConnectionData.writeIntoCursor(value.connection, c); + FfiConverterSequenceTypeFfiMember.writeIntoCursor(value.members, c); + } + allocationSize(value: TypeName): number { + return FfiConverterTypeFfiConnectionData.allocationSize(value.connection) + + FfiConverterSequenceTypeFfiMember.allocationSize(value.members); + + } + }; + return new FFIConverter(); +})(); + +export enum FfiJoinExclusionReason { + SlotClosed, + UnencryptedInEncryptedRoom, + SenderNotInRoom, + Expired +} + +const FfiConverterTypeFfiJoinExclusionReason = (() => { + type TypeName = FfiJoinExclusionReason; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiJoinExclusionReason.SlotClosed; + case 2: return FfiJoinExclusionReason.UnencryptedInEncryptedRoom; + case 3: return FfiJoinExclusionReason.SenderNotInRoom; + case 4: return FfiJoinExclusionReason.Expired; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiJoinExclusionReason.SlotClosed: return c.writeI32(1); + case FfiJoinExclusionReason.UnencryptedInEncryptedRoom: return c.writeI32(2); + case FfiJoinExclusionReason.SenderNotInRoom: return c.writeI32(3); + case FfiJoinExclusionReason.Expired: return c.writeI32(4); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + +export type FfiExcludedCandidate = { + member: FfiMember, + reason: FfiJoinExclusionReason +} + +/** + * Generated factory for {@link FfiExcludedCandidate} record objects. + */ +export const FfiExcludedCandidate = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiExcludedCandidate = (() => { + type TypeName = FfiExcludedCandidate; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + member: FfiConverterTypeFfiMember.readFromCursor(c), + reason: FfiConverterTypeFfiJoinExclusionReason.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterTypeFfiMember.writeIntoCursor(value.member, c); + FfiConverterTypeFfiJoinExclusionReason.writeIntoCursor(value.reason, c); + } + allocationSize(value: TypeName): number { + return FfiConverterTypeFfiMember.allocationSize(value.member) + + FfiConverterTypeFfiJoinExclusionReason.allocationSize(value.reason); + + } + }; + return new FFIConverter(); +})(); + +export type FfiJoinParams = { + applicationType: string, + /** + * `application["m.call.intent"]`. + */ + intent?: string, + stickyDurationMs: bigint, + keepAliveTimeoutMs: bigint, + /** + * Lifetime when the homeserver refuses delayed events (default 5 min). + */ + degradedLifetimeMs?: bigint, + delegateDelayedLeave: boolean +} + +/** + * Generated factory for {@link FfiJoinParams} record objects. + */ +export const FfiJoinParams = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiJoinParams = (() => { + type TypeName = FfiJoinParams; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + applicationType: FfiConverterString.readFromCursor(c), + intent: FfiConverterOptionalString.readFromCursor(c), + stickyDurationMs: FfiConverterUInt64.readFromCursor(c), + keepAliveTimeoutMs: FfiConverterUInt64.readFromCursor(c), + degradedLifetimeMs: FfiConverterOptionalUInt64.readFromCursor(c), + delegateDelayedLeave: FfiConverterBool.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.applicationType, c); + FfiConverterOptionalString.writeIntoCursor(value.intent, c); + FfiConverterUInt64.writeIntoCursor(value.stickyDurationMs, c); + FfiConverterUInt64.writeIntoCursor(value.keepAliveTimeoutMs, c); + FfiConverterOptionalUInt64.writeIntoCursor(value.degradedLifetimeMs, c); + FfiConverterBool.writeIntoCursor(value.delegateDelayedLeave, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.applicationType) + + FfiConverterOptionalString.allocationSize(value.intent) + + FfiConverterUInt64.allocationSize(value.stickyDurationMs) + + FfiConverterUInt64.allocationSize(value.keepAliveTimeoutMs) + + FfiConverterOptionalUInt64.allocationSize(value.degradedLifetimeMs) + + FfiConverterBool.allocationSize(value.delegateDelayedLeave); + + } + }; + return new FFIConverter(); +})(); + +/** + * Join progress, step by step. + */ +export type FfiJoinProgress = { + hasFetchedTransports: boolean, + hasFetchedInitialMemberList: boolean, + hasCreatedTransportToken: boolean, + hasSentDelayedLeaveEvent: boolean, + hasSentMemberJoinEvent: boolean, + hasDelegatedDelayedEvent: boolean, + hasStartedHeartbeat: boolean +} + +/** + * Generated factory for {@link FfiJoinProgress} record objects. + */ +export const FfiJoinProgress = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiJoinProgress = (() => { + type TypeName = FfiJoinProgress; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + hasFetchedTransports: FfiConverterBool.readFromCursor(c), + hasFetchedInitialMemberList: FfiConverterBool.readFromCursor(c), + hasCreatedTransportToken: FfiConverterBool.readFromCursor(c), + hasSentDelayedLeaveEvent: FfiConverterBool.readFromCursor(c), + hasSentMemberJoinEvent: FfiConverterBool.readFromCursor(c), + hasDelegatedDelayedEvent: FfiConverterBool.readFromCursor(c), + hasStartedHeartbeat: FfiConverterBool.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterBool.writeIntoCursor(value.hasFetchedTransports, c); + FfiConverterBool.writeIntoCursor(value.hasFetchedInitialMemberList, c); + FfiConverterBool.writeIntoCursor(value.hasCreatedTransportToken, c); + FfiConverterBool.writeIntoCursor(value.hasSentDelayedLeaveEvent, c); + FfiConverterBool.writeIntoCursor(value.hasSentMemberJoinEvent, c); + FfiConverterBool.writeIntoCursor(value.hasDelegatedDelayedEvent, c); + FfiConverterBool.writeIntoCursor(value.hasStartedHeartbeat, c); + } + allocationSize(value: TypeName): number { + return FfiConverterBool.allocationSize(value.hasFetchedTransports) + + FfiConverterBool.allocationSize(value.hasFetchedInitialMemberList) + + FfiConverterBool.allocationSize(value.hasCreatedTransportToken) + + FfiConverterBool.allocationSize(value.hasSentDelayedLeaveEvent) + + FfiConverterBool.allocationSize(value.hasSentMemberJoinEvent) + + FfiConverterBool.allocationSize(value.hasDelegatedDelayedEvent) + + FfiConverterBool.allocationSize(value.hasStartedHeartbeat); + + } + }; + return new FFIConverter(); +})(); + +export type FfiLivekitToken = { + jwt: string, + /** + * The SFU websocket URL from the response, when it returned one. + */ + url?: string +} + +/** + * Generated factory for {@link FfiLivekitToken} record objects. + */ +export const FfiLivekitToken = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiLivekitToken = (() => { + type TypeName = FfiLivekitToken; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + jwt: FfiConverterString.readFromCursor(c), + url: FfiConverterOptionalString.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.jwt, c); + FfiConverterOptionalString.writeIntoCursor(value.url, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.jwt) + + FfiConverterOptionalString.allocationSize(value.url); + + } + }; + return new FFIConverter(); +})(); + +/** + * See `driver::LivekitTokenRequest`: the driver fetches the OpenID token + * itself and posts `{ room_id, slot_id, openid_token, member }` to + * `{url}/get_token` — or, with `legacy_sfu_get`, `{ room: room_id, + * openid_token, device_id }` to `{url}/sfu/get`. + */ +export type FfiLivekitTokenRequest = { + url: string, + roomId: string, + slotId: string, + /** + * MSC4195 member claims `{ id, claimed_user_id, claimed_device_id }`. + */ + memberJson: string, + legacySfuGet: boolean +} + +/** + * Generated factory for {@link FfiLivekitTokenRequest} record objects. + */ +export const FfiLivekitTokenRequest = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiLivekitTokenRequest = (() => { + type TypeName = FfiLivekitTokenRequest; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + url: FfiConverterString.readFromCursor(c), + roomId: FfiConverterString.readFromCursor(c), + slotId: FfiConverterString.readFromCursor(c), + memberJson: FfiConverterString.readFromCursor(c), + legacySfuGet: FfiConverterBool.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.url, c); + FfiConverterString.writeIntoCursor(value.roomId, c); + FfiConverterString.writeIntoCursor(value.slotId, c); + FfiConverterString.writeIntoCursor(value.memberJson, c); + FfiConverterBool.writeIntoCursor(value.legacySfuGet, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.url) + + FfiConverterString.allocationSize(value.roomId) + + FfiConverterString.allocationSize(value.slotId) + + FfiConverterString.allocationSize(value.memberJson) + + FfiConverterBool.allocationSize(value.legacySfuGet); + + } + }; + return new FFIConverter(); +})(); + +export type FfiMediaKey = { + memberId: string, + key: ArrayBuffer, + index: number, + creationTsMs: bigint +} + +/** + * Generated factory for {@link FfiMediaKey} record objects. + */ +export const FfiMediaKey = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiMediaKey = (() => { + type TypeName = FfiMediaKey; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + memberId: FfiConverterString.readFromCursor(c), + key: FfiConverterArrayBuffer.readFromCursor(c), + index: FfiConverterUInt8.readFromCursor(c), + creationTsMs: FfiConverterUInt64.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.memberId, c); + FfiConverterArrayBuffer.writeIntoCursor(value.key, c); + FfiConverterUInt8.writeIntoCursor(value.index, c); + FfiConverterUInt64.writeIntoCursor(value.creationTsMs, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.memberId) + + FfiConverterArrayBuffer.allocationSize(value.key) + + FfiConverterUInt8.allocationSize(value.index) + + FfiConverterUInt64.allocationSize(value.creationTsMs); + + } + }; + return new FFIConverter(); +})(); + +/** + * Why an inbound media key was discarded — the answer to "why can't I hear + * them?", which the crate computes and used to drop on the floor. + */ +export enum FfiKeyRejection { + Cleartext, + UnknownOrigin, + SenderMismatch, + DeviceMismatch, + UnattributableMember, + NotCrossSigned, + WrongRoom, + Outdated, + NotManagingKeys +} + +const FfiConverterTypeFfiKeyRejection = (() => { + type TypeName = FfiKeyRejection; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiKeyRejection.Cleartext; + case 2: return FfiKeyRejection.UnknownOrigin; + case 3: return FfiKeyRejection.SenderMismatch; + case 4: return FfiKeyRejection.DeviceMismatch; + case 5: return FfiKeyRejection.UnattributableMember; + case 6: return FfiKeyRejection.NotCrossSigned; + case 7: return FfiKeyRejection.WrongRoom; + case 8: return FfiKeyRejection.Outdated; + case 9: return FfiKeyRejection.NotManagingKeys; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiKeyRejection.Cleartext: return c.writeI32(1); + case FfiKeyRejection.UnknownOrigin: return c.writeI32(2); + case FfiKeyRejection.SenderMismatch: return c.writeI32(3); + case FfiKeyRejection.DeviceMismatch: return c.writeI32(4); + case FfiKeyRejection.UnattributableMember: return c.writeI32(5); + case FfiKeyRejection.NotCrossSigned: return c.writeI32(6); + case FfiKeyRejection.WrongRoom: return c.writeI32(7); + case FfiKeyRejection.Outdated: return c.writeI32(8); + case FfiKeyRejection.NotManagingKeys: return c.writeI32(9); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + +/** + * Who can hear whom, for one tile. Two independent booleans: they fail for + * different reasons and a UI renders them in different places. + */ +export type FfiMediaKeyState = { + /** + * They hold our current key: they can decrypt us. + */ + holdsOurKey: boolean, + /** + * We hold theirs: we can decrypt them. + */ + haveTheirKey: boolean, + /** + * Why their most recent key was discarded, while we still lack one. + */ + rejection?: FfiKeyRejection +} + +/** + * Generated factory for {@link FfiMediaKeyState} record objects. + */ +export const FfiMediaKeyState = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiMediaKeyState = (() => { + type TypeName = FfiMediaKeyState; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + holdsOurKey: FfiConverterBool.readFromCursor(c), + haveTheirKey: FfiConverterBool.readFromCursor(c), + rejection: FfiConverterOptionalTypeFfiKeyRejection.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterBool.writeIntoCursor(value.holdsOurKey, c); + FfiConverterBool.writeIntoCursor(value.haveTheirKey, c); + FfiConverterOptionalTypeFfiKeyRejection.writeIntoCursor(value.rejection, c); + } + allocationSize(value: TypeName): number { + return FfiConverterBool.allocationSize(value.holdsOurKey) + + FfiConverterBool.allocationSize(value.haveTheirKey) + + FfiConverterOptionalTypeFfiKeyRejection.allocationSize(value.rejection); + + } + }; + return new FFIConverter(); +})(); + +export enum FfiMembershipState { + /** + * In the session's joined projection. + */ + Joined, + /** + * Left the session but still holds a not-yet-rotated copy of our media + * key — render as "leaving / may still be listening" until rotation + * settles. + */ + LeftWithKeys +} + +const FfiConverterTypeFfiMembershipState = (() => { + type TypeName = FfiMembershipState; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiMembershipState.Joined; + case 2: return FfiMembershipState.LeftWithKeys; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiMembershipState.Joined: return c.writeI32(1); + case FfiMembershipState.LeftWithKeys: return c.writeI32(2); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + +/** + * One entry of the membership list — everything needed to render a tile + * and later attach its media from the LK rooms. + */ +export type FfiMembership = { + member: FfiMember, + state: FfiMembershipState, + /** + * `service_url`s (the connection key, `FfiConnectionData::service_url`) + * of the connections this member publishes on — the LK room(s) carrying + * their media. Empty for receive-only members and `LeftWithKeys` + * entries. + */ + connections: Array, + /** + * Participant identity inside those LK rooms (MSC4195 pseudonymous + * hash; `{user}:{device}` in legacy compat mode). + */ + transportIdentity?: string, + /** + * Whether this member and we can hear each other. `None` when the call + * does not manage media keys, for our own tile, and while not joined. + */ + mediaKey?: FfiMediaKeyState +} + +/** + * Generated factory for {@link FfiMembership} record objects. + */ +export const FfiMembership = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiMembership = (() => { + type TypeName = FfiMembership; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + member: FfiConverterTypeFfiMember.readFromCursor(c), + state: FfiConverterTypeFfiMembershipState.readFromCursor(c), + connections: FfiConverterSequenceString.readFromCursor(c), + transportIdentity: FfiConverterOptionalString.readFromCursor(c), + mediaKey: FfiConverterOptionalTypeFfiMediaKeyState.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterTypeFfiMember.writeIntoCursor(value.member, c); + FfiConverterTypeFfiMembershipState.writeIntoCursor(value.state, c); + FfiConverterSequenceString.writeIntoCursor(value.connections, c); + FfiConverterOptionalString.writeIntoCursor(value.transportIdentity, c); + FfiConverterOptionalTypeFfiMediaKeyState.writeIntoCursor(value.mediaKey, c); + } + allocationSize(value: TypeName): number { + return FfiConverterTypeFfiMember.allocationSize(value.member) + + FfiConverterTypeFfiMembershipState.allocationSize(value.state) + + FfiConverterSequenceString.allocationSize(value.connections) + + FfiConverterOptionalString.allocationSize(value.transportIdentity) + + FfiConverterOptionalTypeFfiMediaKeyState.allocationSize(value.mediaKey); + + } + }; + return new FFIConverter(); +})(); + +/** + * Our sticky membership event on the server (MSC4354). + */ +export type FfiMembershipPublication = { + lifetimeMs: bigint, + lastPublishedTs: bigint, + /** + * `last_published_ts + lifetime_ms` — when the server drops us if no + * refresh lands. + */ + expiresAtTs: bigint, + refreshFailingSinceTs?: bigint, + lastRefreshError?: string +} + +/** + * Generated factory for {@link FfiMembershipPublication} record objects. + */ +export const FfiMembershipPublication = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiMembershipPublication = (() => { + type TypeName = FfiMembershipPublication; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + lifetimeMs: FfiConverterUInt64.readFromCursor(c), + lastPublishedTs: FfiConverterUInt64.readFromCursor(c), + expiresAtTs: FfiConverterUInt64.readFromCursor(c), + refreshFailingSinceTs: FfiConverterOptionalUInt64.readFromCursor(c), + lastRefreshError: FfiConverterOptionalString.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterUInt64.writeIntoCursor(value.lifetimeMs, c); + FfiConverterUInt64.writeIntoCursor(value.lastPublishedTs, c); + FfiConverterUInt64.writeIntoCursor(value.expiresAtTs, c); + FfiConverterOptionalUInt64.writeIntoCursor(value.refreshFailingSinceTs, c); + FfiConverterOptionalString.writeIntoCursor(value.lastRefreshError, c); + } + allocationSize(value: TypeName): number { + return FfiConverterUInt64.allocationSize(value.lifetimeMs) + + FfiConverterUInt64.allocationSize(value.lastPublishedTs) + + FfiConverterUInt64.allocationSize(value.expiresAtTs) + + FfiConverterOptionalUInt64.allocationSize(value.refreshFailingSinceTs) + + FfiConverterOptionalString.allocationSize(value.lastRefreshError); + + } + }; + return new FFIConverter(); +})(); + +/** + * Pre-2026 Element Call interop, selected per call (session read side + + * own-membership write side). + */ +export enum FfiElementCallCompat { + Off, + StickyEvents, + StateEvents +} + +const FfiConverterTypeFfiElementCallCompat = (() => { + type TypeName = FfiElementCallCompat; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiElementCallCompat.Off; + case 2: return FfiElementCallCompat.StickyEvents; + case 3: return FfiElementCallCompat.StateEvents; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiElementCallCompat.Off: return c.writeI32(1); + case FfiElementCallCompat.StickyEvents: return c.writeI32(2); + case FfiElementCallCompat.StateEvents: return c.writeI32(3); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + +/** + * Per-manager configuration: the compat dialect plus the encryption knobs a + * host must be able to set (a room without a slot negotiates nothing, so + * the local defaults decide). + */ +export type FfiParticipationConfig = { + compat: FfiElementCallCompat, + /** + * Whether to exchange media keys when the slot prescribes nothing (no + * slot in the room). A slot's negotiated encryption overrides it. + */ + manageMediaKeys: boolean, + /** + * MSC4153: discard media keys from a sending device the host did not + * report as cross-signed by its owner. Hosts that cannot evaluate that + * (or whose peers may be unverified guests) turn it off. + */ + requireCrossSignedSender: boolean, + /** + * Wait this long after sending a rotated key before encrypting with it, + * so slow peers have it before the first frame arrives. + */ + useKeyDelayMs: bigint +} + +/** + * Generated factory for {@link FfiParticipationConfig} record objects. + */ +export const FfiParticipationConfig = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiParticipationConfig = (() => { + type TypeName = FfiParticipationConfig; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + compat: FfiConverterTypeFfiElementCallCompat.readFromCursor(c), + manageMediaKeys: FfiConverterBool.readFromCursor(c), + requireCrossSignedSender: FfiConverterBool.readFromCursor(c), + useKeyDelayMs: FfiConverterUInt64.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterTypeFfiElementCallCompat.writeIntoCursor(value.compat, c); + FfiConverterBool.writeIntoCursor(value.manageMediaKeys, c); + FfiConverterBool.writeIntoCursor(value.requireCrossSignedSender, c); + FfiConverterUInt64.writeIntoCursor(value.useKeyDelayMs, c); + } + allocationSize(value: TypeName): number { + return FfiConverterTypeFfiElementCallCompat.allocationSize(value.compat) + + FfiConverterBool.allocationSize(value.manageMediaKeys) + + FfiConverterBool.allocationSize(value.requireCrossSignedSender) + + FfiConverterUInt64.allocationSize(value.useKeyDelayMs); + + } + }; + return new FFIConverter(); +})(); + +export type FfiSendEventResponse = { + eventId?: string, + delayId?: string +} + +/** + * Generated factory for {@link FfiSendEventResponse} record objects. + */ +export const FfiSendEventResponse = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiSendEventResponse = (() => { + type TypeName = FfiSendEventResponse; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + eventId: FfiConverterOptionalString.readFromCursor(c), + delayId: FfiConverterOptionalString.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterOptionalString.writeIntoCursor(value.eventId, c); + FfiConverterOptionalString.writeIntoCursor(value.delayId, c); + } + allocationSize(value: TypeName): number { + return FfiConverterOptionalString.allocationSize(value.eventId) + + FfiConverterOptionalString.allocationSize(value.delayId); + + } + }; + return new FFIConverter(); +})(); + +/** + * One of the room-state / timeline reads the session's seed makes. A read + * that failed leaves its condition *unknown*, which is not the same as the + * condition being absent. + */ +export enum FfiSessionRead { + Slot, + RoomEncryption, + RoomMembers, + MemberEvents +} + +const FfiConverterTypeFfiSessionRead = (() => { + type TypeName = FfiSessionRead; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiSessionRead.Slot; + case 2: return FfiSessionRead.RoomEncryption; + case 3: return FfiSessionRead.RoomMembers; + case 4: return FfiSessionRead.MemberEvents; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiSessionRead.Slot: return c.writeI32(1); + case FfiSessionRead.RoomEncryption: return c.writeI32(2); + case FfiSessionRead.RoomMembers: return c.writeI32(3); + case FfiSessionRead.MemberEvents: return c.writeI32(4); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + +/** + * One session as a plain value — what [`compute_sessions_from_events`] + * returns. The convenience data the room-list / header / lobby / room_info + * computation needs is precomputed into fields (records carry no methods). + */ +export type FfiSessionSnapshot = { + roomId: string, + slotId: string, + members: Array, + memberCount: number, + isActive: boolean, + /** + * `origin_server_ts` of the earliest joined membership, while active. + */ + startTs?: bigint, + applicationType?: string, + /** + * `None` while no slot state was supplied (condition unenforced). + */ + slotOpen?: boolean, + /** + * The slot-prescribed encryption decision; `None` while unknown. + * + * Read it together with `failed_reads`: `None` with an empty + * `failed_reads` means "this call is not encrypted", `None` with + * `Slot` in it means "we could not find out" — the difference between + * rendering an open padlock and rendering nothing. + */ + encrypted?: boolean, + /** + * `true` once the live session finished seeding (even after read + * failures); always `true` for a statically computed snapshot. + */ + seeded: boolean, + /** + * Seed reads that failed, so an absent value can be told from an + * unknown one. Empty is the healthy case, and an entry disappears once + * a live state update supplies that value. + */ + failedReads: Array, + /** + * Member events that landed but are not in the joined projection, with + * the reason — the load-bearing diagnostics for "why can nobody see + * me?". Find your own entry with `own_member_id()`. + */ + excludedCandidates: Array +} + +/** + * Generated factory for {@link FfiSessionSnapshot} record objects. + */ +export const FfiSessionSnapshot = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiSessionSnapshot = (() => { + type TypeName = FfiSessionSnapshot; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + roomId: FfiConverterString.readFromCursor(c), + slotId: FfiConverterString.readFromCursor(c), + members: FfiConverterSequenceTypeFfiMember.readFromCursor(c), + memberCount: FfiConverterUInt32.readFromCursor(c), + isActive: FfiConverterBool.readFromCursor(c), + startTs: FfiConverterOptionalUInt64.readFromCursor(c), + applicationType: FfiConverterOptionalString.readFromCursor(c), + slotOpen: FfiConverterOptionalBoolean.readFromCursor(c), + encrypted: FfiConverterOptionalBoolean.readFromCursor(c), + seeded: FfiConverterBool.readFromCursor(c), + failedReads: FfiConverterSequenceTypeFfiSessionRead.readFromCursor(c), + excludedCandidates: FfiConverterSequenceTypeFfiExcludedCandidate.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.roomId, c); + FfiConverterString.writeIntoCursor(value.slotId, c); + FfiConverterSequenceTypeFfiMember.writeIntoCursor(value.members, c); + FfiConverterUInt32.writeIntoCursor(value.memberCount, c); + FfiConverterBool.writeIntoCursor(value.isActive, c); + FfiConverterOptionalUInt64.writeIntoCursor(value.startTs, c); + FfiConverterOptionalString.writeIntoCursor(value.applicationType, c); + FfiConverterOptionalBoolean.writeIntoCursor(value.slotOpen, c); + FfiConverterOptionalBoolean.writeIntoCursor(value.encrypted, c); + FfiConverterBool.writeIntoCursor(value.seeded, c); + FfiConverterSequenceTypeFfiSessionRead.writeIntoCursor(value.failedReads, c); + FfiConverterSequenceTypeFfiExcludedCandidate.writeIntoCursor(value.excludedCandidates, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.roomId) + + FfiConverterString.allocationSize(value.slotId) + + FfiConverterSequenceTypeFfiMember.allocationSize(value.members) + + FfiConverterUInt32.allocationSize(value.memberCount) + + FfiConverterBool.allocationSize(value.isActive) + + FfiConverterOptionalUInt64.allocationSize(value.startTs) + + FfiConverterOptionalString.allocationSize(value.applicationType) + + FfiConverterOptionalBoolean.allocationSize(value.slotOpen) + + FfiConverterOptionalBoolean.allocationSize(value.encrypted) + + FfiConverterBool.allocationSize(value.seeded) + + FfiConverterSequenceTypeFfiSessionRead.allocationSize(value.failedReads) + + FfiConverterSequenceTypeFfiExcludedCandidate.allocationSize(value.excludedCandidates); + + } + }; + return new FFIConverter(); +})(); + +export type FfiToDeviceRecipient = { + userId: string, + deviceId: string +} + +/** + * Generated factory for {@link FfiToDeviceRecipient} record objects. + */ +export const FfiToDeviceRecipient = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiToDeviceRecipient = (() => { + type TypeName = FfiToDeviceRecipient; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + userId: FfiConverterString.readFromCursor(c), + deviceId: FfiConverterString.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterString.writeIntoCursor(value.userId, c); + FfiConverterString.writeIntoCursor(value.deviceId, c); + } + allocationSize(value: TypeName): number { + return FfiConverterString.allocationSize(value.userId) + + FfiConverterString.allocationSize(value.deviceId); + + } + }; + return new FFIConverter(); +})(); + +export type FfiToDeviceDelivery = { + recipient: FfiToDeviceRecipient, + error?: string +} + +/** + * Generated factory for {@link FfiToDeviceDelivery} record objects. + */ +export const FfiToDeviceDelivery = (() => { + const defaults = () => ({ + }); + const create = (() => { + return uniffiCreateRecord>(defaults); + })(); + return Object.freeze({ + create, + new: create, + defaults: () => Object.freeze(defaults()) as Partial, + }); +})(); + +const FfiConverterTypeFfiToDeviceDelivery = (() => { + type TypeName = FfiToDeviceDelivery; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + return { + recipient: FfiConverterTypeFfiToDeviceRecipient.readFromCursor(c), + error: FfiConverterOptionalString.readFromCursor(c) + }; + } + writeIntoCursor(value: TypeName, c: Cursor): void { + FfiConverterTypeFfiToDeviceRecipient.writeIntoCursor(value.recipient, c); + FfiConverterOptionalString.writeIntoCursor(value.error, c); + } + allocationSize(value: TypeName): number { + return FfiConverterTypeFfiToDeviceRecipient.allocationSize(value.recipient) + + FfiConverterOptionalString.allocationSize(value.error); + + } + }; + return new FFIConverter(); +})(); + +/** + * Which pump stopped, for [`FfiDisconnectCause::ManagerStopped`]. + */ +export enum FfiComponent { + Session, + OwnMembership, + Connections, + Encryption, + Participation +} + +const FfiConverterTypeFfiComponent = (() => { + type TypeName = FfiComponent; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiComponent.Session; + case 2: return FfiComponent.OwnMembership; + case 3: return FfiComponent.Connections; + case 4: return FfiComponent.Encryption; + case 5: return FfiComponent.Participation; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiComponent.Session: return c.writeI32(1); + case FfiComponent.OwnMembership: return c.writeI32(2); + case FfiComponent.Connections: return c.writeI32(3); + case FfiComponent.Encryption: return c.writeI32(4); + case FfiComponent.Participation: return c.writeI32(5); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + +/** + * What became of the dead man's switch when we left. A failed cancel still + * leaves us out of the call — the delay is itself a leave — but a stray + * delayed event of ours may land afterwards. + */ +export enum FfiDelayedLeaveOutcome { + Cancelled, + MayStillFire +} + +const FfiConverterTypeFfiDelayedLeaveOutcome = (() => { + type TypeName = FfiDelayedLeaveOutcome; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiDelayedLeaveOutcome.Cancelled; + case 2: return FfiDelayedLeaveOutcome.MayStillFire; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiDelayedLeaveOutcome.Cancelled: return c.writeI32(1); + case FfiDelayedLeaveOutcome.MayStillFire: return c.writeI32(2); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + + +// Enum: FfiJoinError +export enum FfiJoinError_Tags { + AlreadyJoined = "AlreadyJoined", + InvalidParams = "InvalidParams", + SlotClosed = "SlotClosed", + NoTransport = "NoTransport", + TokenRefused = "TokenRefused", + EncryptionSetup = "EncryptionSetup", + Driver = "Driver" +} +/** + * Why a join failed. Typed so a host can decide what to offer next: + * `NoTransport` is a configuration problem, `TokenRefused` may be worth a + * retry. + */ +export const FfiJoinError = (() => { + + type AlreadyJoined__interface = { + tag: FfiJoinError_Tags.AlreadyJoined + }; + class AlreadyJoined_ extends UniffiEnum implements AlreadyJoined__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiJoinError"; + readonly tag = FfiJoinError_Tags.AlreadyJoined; + constructor() { + super("FfiJoinError", "AlreadyJoined"); + } + + static new(): AlreadyJoined_ { + return new AlreadyJoined_(); + } + + static instanceOf(obj: any): obj is AlreadyJoined_ { + return obj.tag === FfiJoinError_Tags.AlreadyJoined; + } + + } + + type InvalidParams__interface = { + tag: FfiJoinError_Tags.InvalidParams; + inner: +Readonly<{message: string}> + }; + class InvalidParams_ extends UniffiEnum implements InvalidParams__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiJoinError"; + readonly tag = FfiJoinError_Tags.InvalidParams; + readonly inner: +Readonly<{message: string}>; + constructor( +inner: {message: string }) { + super("FfiJoinError", "InvalidParams"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {message: string }): InvalidParams_ { + return new InvalidParams_(inner); + } + + static instanceOf(obj: any): obj is InvalidParams_ { + return obj.tag === FfiJoinError_Tags.InvalidParams; + } + + } + + type SlotClosed__interface = { + tag: FfiJoinError_Tags.SlotClosed + }; + class SlotClosed_ extends UniffiEnum implements SlotClosed__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiJoinError"; + readonly tag = FfiJoinError_Tags.SlotClosed; + constructor() { + super("FfiJoinError", "SlotClosed"); + } + + static new(): SlotClosed_ { + return new SlotClosed_(); + } + + static instanceOf(obj: any): obj is SlotClosed_ { + return obj.tag === FfiJoinError_Tags.SlotClosed; + } + + } + + type NoTransport__interface = { + tag: FfiJoinError_Tags.NoTransport; + inner: +Readonly<{message: string}> + }; + class NoTransport_ extends UniffiEnum implements NoTransport__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiJoinError"; + readonly tag = FfiJoinError_Tags.NoTransport; + readonly inner: +Readonly<{message: string}>; + constructor( +inner: {message: string }) { + super("FfiJoinError", "NoTransport"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {message: string }): NoTransport_ { + return new NoTransport_(inner); + } + + static instanceOf(obj: any): obj is NoTransport_ { + return obj.tag === FfiJoinError_Tags.NoTransport; + } + + } + + type TokenRefused__interface = { + tag: FfiJoinError_Tags.TokenRefused; + inner: +Readonly<{message: string}> + }; + class TokenRefused_ extends UniffiEnum implements TokenRefused__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiJoinError"; + readonly tag = FfiJoinError_Tags.TokenRefused; + readonly inner: +Readonly<{message: string}>; + constructor( +inner: {message: string }) { + super("FfiJoinError", "TokenRefused"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {message: string }): TokenRefused_ { + return new TokenRefused_(inner); + } + + static instanceOf(obj: any): obj is TokenRefused_ { + return obj.tag === FfiJoinError_Tags.TokenRefused; + } + + } + + type EncryptionSetup__interface = { + tag: FfiJoinError_Tags.EncryptionSetup; + inner: +Readonly<{message: string}> + }; + class EncryptionSetup_ extends UniffiEnum implements EncryptionSetup__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiJoinError"; + readonly tag = FfiJoinError_Tags.EncryptionSetup; + readonly inner: +Readonly<{message: string}>; + constructor( +inner: {message: string }) { + super("FfiJoinError", "EncryptionSetup"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {message: string }): EncryptionSetup_ { + return new EncryptionSetup_(inner); + } + + static instanceOf(obj: any): obj is EncryptionSetup_ { + return obj.tag === FfiJoinError_Tags.EncryptionSetup; + } + + } + + type Driver__interface = { + tag: FfiJoinError_Tags.Driver; + inner: +Readonly<{message: string}> + }; + class Driver_ extends UniffiEnum implements Driver__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiJoinError"; + readonly tag = FfiJoinError_Tags.Driver; + readonly inner: +Readonly<{message: string}>; + constructor( +inner: {message: string }) { + super("FfiJoinError", "Driver"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {message: string }): Driver_ { + return new Driver_(inner); + } + + static instanceOf(obj: any): obj is Driver_ { + return obj.tag === FfiJoinError_Tags.Driver; + } + + } + + function instanceOf(obj: any): obj is FfiJoinError { + return obj[uniffiTypeNameSymbol] === "FfiJoinError"; + } + + return Object.freeze({ + instanceOf, + AlreadyJoined: AlreadyJoined_, + InvalidParams: InvalidParams_, + SlotClosed: SlotClosed_, + NoTransport: NoTransport_, + TokenRefused: TokenRefused_, + EncryptionSetup: EncryptionSetup_, + Driver: Driver_ + }); + +})(); +/** + * Why a join failed. Typed so a host can decide what to offer next: + * `NoTransport` is a configuration problem, `TokenRefused` may be worth a + * retry. + */ +export type FfiJoinError = InstanceType< + typeof FfiJoinError['AlreadyJoined' | 'InvalidParams' | 'SlotClosed' | 'NoTransport' | 'TokenRefused' | 'EncryptionSetup' | 'Driver'] +>; + +// FfiConverter for enum FfiJoinError +const FfiConverterTypeFfiJoinError = (() => { + type TypeName = FfiJoinError; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new FfiJoinError.AlreadyJoined(); + case 2: return new FfiJoinError.InvalidParams({message: FfiConverterString.readFromCursor(c) }); + case 3: return new FfiJoinError.SlotClosed(); + case 4: return new FfiJoinError.NoTransport({message: FfiConverterString.readFromCursor(c) }); + case 5: return new FfiJoinError.TokenRefused({message: FfiConverterString.readFromCursor(c) }); + case 6: return new FfiJoinError.EncryptionSetup({message: FfiConverterString.readFromCursor(c) }); + case 7: return new FfiJoinError.Driver({message: FfiConverterString.readFromCursor(c) }); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case FfiJoinError_Tags.AlreadyJoined: { + c.writeI32(1); + return; + } + case FfiJoinError_Tags.InvalidParams: { + c.writeI32(2); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner.message, c); + return; + } + case FfiJoinError_Tags.SlotClosed: { + c.writeI32(3); + return; + } + case FfiJoinError_Tags.NoTransport: { + c.writeI32(4); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner.message, c); + return; + } + case FfiJoinError_Tags.TokenRefused: { + c.writeI32(5); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner.message, c); + return; + } + case FfiJoinError_Tags.EncryptionSetup: { + c.writeI32(6); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner.message, c); + return; + } + case FfiJoinError_Tags.Driver: { + c.writeI32(7); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner.message, c); + return; + } + default: + // Throwing from here means that FfiJoinError_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case FfiJoinError_Tags.AlreadyJoined: { + return 4; + } + case FfiJoinError_Tags.InvalidParams: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner.message); + return size; + } + case FfiJoinError_Tags.SlotClosed: { + return 4; + } + case FfiJoinError_Tags.NoTransport: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner.message); + return size; + } + case FfiJoinError_Tags.TokenRefused: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner.message); + return size; + } + case FfiJoinError_Tags.EncryptionSetup: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner.message); + return size; + } + case FfiJoinError_Tags.Driver: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner.message); + return size; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + + +// Enum: FfiDisconnectCause +export enum FfiDisconnectCause_Tags { + NeverJoined = "NeverJoined", + LeftByHost = "LeftByHost", + SlotClosed = "SlotClosed", + JoinFailed = "JoinFailed", + ManagerStopped = "ManagerStopped" +} +/** + * Why we are not in a call. Terminal by construction: unlike an impairment, + * none of these clears on its own. + */ +export const FfiDisconnectCause = (() => { + + type NeverJoined__interface = { + tag: FfiDisconnectCause_Tags.NeverJoined + }; + /** + * No join has been attempted on this manager. + */ + class NeverJoined_ extends UniffiEnum implements NeverJoined__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiDisconnectCause"; + readonly tag = FfiDisconnectCause_Tags.NeverJoined; + constructor() { + super("FfiDisconnectCause", "NeverJoined"); + } + + static new(): NeverJoined_ { + return new NeverJoined_(); + } + + static instanceOf(obj: any): obj is NeverJoined_ { + return obj.tag === FfiDisconnectCause_Tags.NeverJoined; + } + + } + + type LeftByHost__interface = { + tag: FfiDisconnectCause_Tags.LeftByHost; + inner: +Readonly<{code?: string; reason?: string}> + }; + /** + * The host called `leave()`. + */ + class LeftByHost_ extends UniffiEnum implements LeftByHost__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiDisconnectCause"; + readonly tag = FfiDisconnectCause_Tags.LeftByHost; + readonly inner: +Readonly<{code?: string; reason?: string}>; + constructor( +inner: {code?: string; reason?: string }) { + super("FfiDisconnectCause", "LeftByHost"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {code?: string; reason?: string }): LeftByHost_ { + return new LeftByHost_(inner); + } + + static instanceOf(obj: any): obj is LeftByHost_ { + return obj.tag === FfiDisconnectCause_Tags.LeftByHost; + } + + } + + type SlotClosed__interface = { + tag: FfiDisconnectCause_Tags.SlotClosed + }; + /** + * The slot was closed under us and the machine left on its own. + */ + class SlotClosed_ extends UniffiEnum implements SlotClosed__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiDisconnectCause"; + readonly tag = FfiDisconnectCause_Tags.SlotClosed; + constructor() { + super("FfiDisconnectCause", "SlotClosed"); + } + + static new(): SlotClosed_ { + return new SlotClosed_(); + } + + static instanceOf(obj: any): obj is SlotClosed_ { + return obj.tag === FfiDisconnectCause_Tags.SlotClosed; + } + + } + + type JoinFailed__interface = { + tag: FfiDisconnectCause_Tags.JoinFailed; + inner: +Readonly<{atTs: bigint; progress: FfiJoinProgress; error: FfiJoinError}> + }; + /** + * `join()` failed; the participation never started. `progress` says how + * far it got. + */ + class JoinFailed_ extends UniffiEnum implements JoinFailed__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiDisconnectCause"; + readonly tag = FfiDisconnectCause_Tags.JoinFailed; + readonly inner: +Readonly<{atTs: bigint; progress: FfiJoinProgress; error: FfiJoinError}>; + constructor( +inner: {atTs: bigint; progress: FfiJoinProgress; error: FfiJoinError }) { + super("FfiDisconnectCause", "JoinFailed"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {atTs: bigint; progress: FfiJoinProgress; error: FfiJoinError }): JoinFailed_ { + return new JoinFailed_(inner); + } + + static instanceOf(obj: any): obj is JoinFailed_ { + return obj.tag === FfiDisconnectCause_Tags.JoinFailed; + } + + } + + type ManagerStopped__interface = { + tag: FfiDisconnectCause_Tags.ManagerStopped; + inner: +Readonly<{component: FfiComponent}> + }; + /** + * A pump stopped. The manager is dead and will not recover; build a new + * one. + */ + class ManagerStopped_ extends UniffiEnum implements ManagerStopped__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiDisconnectCause"; + readonly tag = FfiDisconnectCause_Tags.ManagerStopped; + readonly inner: +Readonly<{component: FfiComponent}>; + constructor( +inner: {component: FfiComponent }) { + super("FfiDisconnectCause", "ManagerStopped"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {component: FfiComponent }): ManagerStopped_ { + return new ManagerStopped_(inner); + } + + static instanceOf(obj: any): obj is ManagerStopped_ { + return obj.tag === FfiDisconnectCause_Tags.ManagerStopped; + } + + } + + function instanceOf(obj: any): obj is FfiDisconnectCause { + return obj[uniffiTypeNameSymbol] === "FfiDisconnectCause"; + } + + return Object.freeze({ + instanceOf, + NeverJoined: NeverJoined_, + LeftByHost: LeftByHost_, + SlotClosed: SlotClosed_, + JoinFailed: JoinFailed_, + ManagerStopped: ManagerStopped_ + }); + +})(); +/** + * Why we are not in a call. Terminal by construction: unlike an impairment, + * none of these clears on its own. + */ +export type FfiDisconnectCause = InstanceType< + typeof FfiDisconnectCause['NeverJoined' | 'LeftByHost' | 'SlotClosed' | 'JoinFailed' | 'ManagerStopped'] +>; + +// FfiConverter for enum FfiDisconnectCause +const FfiConverterTypeFfiDisconnectCause = (() => { + type TypeName = FfiDisconnectCause; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new FfiDisconnectCause.NeverJoined(); + case 2: return new FfiDisconnectCause.LeftByHost({code: FfiConverterOptionalString.readFromCursor(c), reason: FfiConverterOptionalString.readFromCursor(c) }); + case 3: return new FfiDisconnectCause.SlotClosed(); + case 4: return new FfiDisconnectCause.JoinFailed({atTs: FfiConverterUInt64.readFromCursor(c), progress: FfiConverterTypeFfiJoinProgress.readFromCursor(c), error: FfiConverterTypeFfiJoinError.readFromCursor(c) }); + case 5: return new FfiDisconnectCause.ManagerStopped({component: FfiConverterTypeFfiComponent.readFromCursor(c) }); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case FfiDisconnectCause_Tags.NeverJoined: { + c.writeI32(1); + return; + } + case FfiDisconnectCause_Tags.LeftByHost: { + c.writeI32(2); + const inner = value.inner; + FfiConverterOptionalString.writeIntoCursor(inner.code, c); + FfiConverterOptionalString.writeIntoCursor(inner.reason, c); + return; + } + case FfiDisconnectCause_Tags.SlotClosed: { + c.writeI32(3); + return; + } + case FfiDisconnectCause_Tags.JoinFailed: { + c.writeI32(4); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.atTs, c); + FfiConverterTypeFfiJoinProgress.writeIntoCursor(inner.progress, c); + FfiConverterTypeFfiJoinError.writeIntoCursor(inner.error, c); + return; + } + case FfiDisconnectCause_Tags.ManagerStopped: { + c.writeI32(5); + const inner = value.inner; + FfiConverterTypeFfiComponent.writeIntoCursor(inner.component, c); + return; + } + default: + // Throwing from here means that FfiDisconnectCause_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case FfiDisconnectCause_Tags.NeverJoined: { + return 4; + } + case FfiDisconnectCause_Tags.LeftByHost: { + const inner = value.inner; + let size = 4; + size += FfiConverterOptionalString.allocationSize(inner.code); + size += FfiConverterOptionalString.allocationSize(inner.reason); + return size; + } + case FfiDisconnectCause_Tags.SlotClosed: { + return 4; + } + case FfiDisconnectCause_Tags.JoinFailed: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.atTs); + size += FfiConverterTypeFfiJoinProgress.allocationSize(inner.progress); + size += FfiConverterTypeFfiJoinError.allocationSize(inner.error); + return size; + } + case FfiDisconnectCause_Tags.ManagerStopped: { + const inner = value.inner; + let size = 4; + size += FfiConverterTypeFfiComponent.allocationSize(inner.component); + return size; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + + +// Enum: FfiEncryptionStatus +export enum FfiEncryptionStatus_Tags { + Joining = "Joining", + Connected = "Connected" +} +/** + * The media-key exchange as a whole. + */ +export const FfiEncryptionStatus = (() => { + + type Joining__interface = { + tag: FfiEncryptionStatus_Tags.Joining; + inner: +Readonly<{hasDistributedInitialKeys: boolean; hasReceivedAllMemberKeys: boolean}> + }; + class Joining_ extends UniffiEnum implements Joining__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiEncryptionStatus"; + readonly tag = FfiEncryptionStatus_Tags.Joining; + readonly inner: +Readonly<{hasDistributedInitialKeys: boolean; hasReceivedAllMemberKeys: boolean}>; + constructor( +inner: {hasDistributedInitialKeys: boolean; hasReceivedAllMemberKeys: boolean }) { + super("FfiEncryptionStatus", "Joining"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {hasDistributedInitialKeys: boolean; hasReceivedAllMemberKeys: boolean }): Joining_ { + return new Joining_(inner); + } + + static instanceOf(obj: any): obj is Joining_ { + return obj.tag === FfiEncryptionStatus_Tags.Joining; + } + + } + + type Connected__interface = { + tag: FfiEncryptionStatus_Tags.Connected; + inner: +Readonly<{leftMembersWithKeys: Array; fullySettled: boolean; lastRotationTs: bigint}> + }; + class Connected_ extends UniffiEnum implements Connected__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiEncryptionStatus"; + readonly tag = FfiEncryptionStatus_Tags.Connected; + readonly inner: +Readonly<{leftMembersWithKeys: Array; fullySettled: boolean; lastRotationTs: bigint}>; + constructor( +inner: {leftMembersWithKeys: Array; fullySettled: boolean; lastRotationTs: bigint }) { + super("FfiEncryptionStatus", "Connected"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {leftMembersWithKeys: Array; fullySettled: boolean; lastRotationTs: bigint }): Connected_ { + return new Connected_(inner); + } + + static instanceOf(obj: any): obj is Connected_ { + return obj.tag === FfiEncryptionStatus_Tags.Connected; + } + + } + + function instanceOf(obj: any): obj is FfiEncryptionStatus { + return obj[uniffiTypeNameSymbol] === "FfiEncryptionStatus"; + } + + return Object.freeze({ + instanceOf, + Joining: Joining_, + Connected: Connected_ + }); + +})(); +/** + * The media-key exchange as a whole. + */ +export type FfiEncryptionStatus = InstanceType< + typeof FfiEncryptionStatus['Joining' | 'Connected'] +>; + +// FfiConverter for enum FfiEncryptionStatus +const FfiConverterTypeFfiEncryptionStatus = (() => { + type TypeName = FfiEncryptionStatus; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new FfiEncryptionStatus.Joining({hasDistributedInitialKeys: FfiConverterBool.readFromCursor(c), hasReceivedAllMemberKeys: FfiConverterBool.readFromCursor(c) }); + case 2: return new FfiEncryptionStatus.Connected({leftMembersWithKeys: FfiConverterSequenceTypeFfiMember.readFromCursor(c), fullySettled: FfiConverterBool.readFromCursor(c), lastRotationTs: FfiConverterUInt64.readFromCursor(c) }); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case FfiEncryptionStatus_Tags.Joining: { + c.writeI32(1); + const inner = value.inner; + FfiConverterBool.writeIntoCursor(inner.hasDistributedInitialKeys, c); + FfiConverterBool.writeIntoCursor(inner.hasReceivedAllMemberKeys, c); + return; + } + case FfiEncryptionStatus_Tags.Connected: { + c.writeI32(2); + const inner = value.inner; + FfiConverterSequenceTypeFfiMember.writeIntoCursor(inner.leftMembersWithKeys, c); + FfiConverterBool.writeIntoCursor(inner.fullySettled, c); + FfiConverterUInt64.writeIntoCursor(inner.lastRotationTs, c); + return; + } + default: + // Throwing from here means that FfiEncryptionStatus_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case FfiEncryptionStatus_Tags.Joining: { + const inner = value.inner; + let size = 4; + size += FfiConverterBool.allocationSize(inner.hasDistributedInitialKeys); + size += FfiConverterBool.allocationSize(inner.hasReceivedAllMemberKeys); + return size; + } + case FfiEncryptionStatus_Tags.Connected: { + const inner = value.inner; + let size = 4; + size += FfiConverterSequenceTypeFfiMember.allocationSize(inner.leftMembersWithKeys); + size += FfiConverterBool.allocationSize(inner.fullySettled); + size += FfiConverterUInt64.allocationSize(inner.lastRotationTs); + return size; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + + +// Enum: FfiEventOrigin +export enum FfiEventOrigin_Tags { + Encrypted = "Encrypted", + Cleartext = "Cleartext", + Unknown = "Unknown" +} +export const FfiEventOrigin = (() => { + + type Encrypted__interface = { + tag: FfiEventOrigin_Tags.Encrypted; + inner: +Readonly<{senderDeviceId?: string}> + }; + class Encrypted_ extends UniffiEnum implements Encrypted__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiEventOrigin"; + readonly tag = FfiEventOrigin_Tags.Encrypted; + readonly inner: +Readonly<{senderDeviceId?: string}>; + constructor( +inner: {senderDeviceId?: string }) { + super("FfiEventOrigin", "Encrypted"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {senderDeviceId?: string }): Encrypted_ { + return new Encrypted_(inner); + } + + static instanceOf(obj: any): obj is Encrypted_ { + return obj.tag === FfiEventOrigin_Tags.Encrypted; + } + + } + + type Cleartext__interface = { + tag: FfiEventOrigin_Tags.Cleartext + }; + class Cleartext_ extends UniffiEnum implements Cleartext__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiEventOrigin"; + readonly tag = FfiEventOrigin_Tags.Cleartext; + constructor() { + super("FfiEventOrigin", "Cleartext"); + } + + static new(): Cleartext_ { + return new Cleartext_(); + } + + static instanceOf(obj: any): obj is Cleartext_ { + return obj.tag === FfiEventOrigin_Tags.Cleartext; + } + + } + + type Unknown__interface = { + tag: FfiEventOrigin_Tags.Unknown + }; + class Unknown_ extends UniffiEnum implements Unknown__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiEventOrigin"; + readonly tag = FfiEventOrigin_Tags.Unknown; + constructor() { + super("FfiEventOrigin", "Unknown"); + } + + static new(): Unknown_ { + return new Unknown_(); + } + + static instanceOf(obj: any): obj is Unknown_ { + return obj.tag === FfiEventOrigin_Tags.Unknown; + } + + } + + function instanceOf(obj: any): obj is FfiEventOrigin { + return obj[uniffiTypeNameSymbol] === "FfiEventOrigin"; + } + + return Object.freeze({ + instanceOf, + Encrypted: Encrypted_, + Cleartext: Cleartext_, + Unknown: Unknown_ + }); + +})(); +export type FfiEventOrigin = InstanceType< + typeof FfiEventOrigin['Encrypted' | 'Cleartext' | 'Unknown'] +>; + +// FfiConverter for enum FfiEventOrigin +const FfiConverterTypeFfiEventOrigin = (() => { + type TypeName = FfiEventOrigin; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new FfiEventOrigin.Encrypted({senderDeviceId: FfiConverterOptionalString.readFromCursor(c) }); + case 2: return new FfiEventOrigin.Cleartext(); + case 3: return new FfiEventOrigin.Unknown(); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case FfiEventOrigin_Tags.Encrypted: { + c.writeI32(1); + const inner = value.inner; + FfiConverterOptionalString.writeIntoCursor(inner.senderDeviceId, c); + return; + } + case FfiEventOrigin_Tags.Cleartext: { + c.writeI32(2); + return; + } + case FfiEventOrigin_Tags.Unknown: { + c.writeI32(3); + return; + } + default: + // Throwing from here means that FfiEventOrigin_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case FfiEventOrigin_Tags.Encrypted: { + const inner = value.inner; + let size = 4; + size += FfiConverterOptionalString.allocationSize(inner.senderDeviceId); + return size; + } + case FfiEventOrigin_Tags.Cleartext: { + return 4; + } + case FfiEventOrigin_Tags.Unknown: { + return 4; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + + +// Enum: FfiImpairment +export enum FfiImpairment_Tags { + HomeserverUnreachable = "HomeserverUnreachable", + KeepAliveRestartFailing = "KeepAliveRestartFailing", + KeepAliveExpired = "KeepAliveExpired", + KeepAliveUnavailable = "KeepAliveUnavailable", + MembershipRefreshFailing = "MembershipRefreshFailing", + OwnMembershipMissing = "OwnMembershipMissing", + OwnMembershipExcluded = "OwnMembershipExcluded", + MediaKeyNotDelivered = "MediaKeyNotDelivered", + MediaKeyNotReceived = "MediaKeyNotReceived", + MediaKeyRejected = "MediaKeyRejected", + ConnectionUnavailable = "ConnectionUnavailable", + ConnectionTokenExpired = "ConnectionTokenExpired", + SessionStateUnread = "SessionStateUnread", + JoinedBeforeSeed = "JoinedBeforeSeed" +} +/** + * A condition that is true right now and that the crate is still working + * on. Every variant clears by itself when the underlying operation + * succeeds — an impairment is never terminal; anything terminal ends the + * participation and appears as [`FfiDisconnectCause`] instead. + * + * A host that renders one warning banner can read this list and nothing + * else; the structured status above is for anything that needs the details. + */ +export const FfiImpairment = (() => { + + type HomeserverUnreachable__interface = { + tag: FfiImpairment_Tags.HomeserverUnreachable; + inner: +Readonly<{sinceTs: bigint}> + }; + /** + * The driver reports the homeserver unreachable; clears when it is back. + */ + class HomeserverUnreachable_ extends UniffiEnum implements HomeserverUnreachable__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.HomeserverUnreachable; + readonly inner: +Readonly<{sinceTs: bigint}>; + constructor( +inner: {sinceTs: bigint }) { + super("FfiImpairment", "HomeserverUnreachable"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {sinceTs: bigint }): HomeserverUnreachable_ { + return new HomeserverUnreachable_(inner); + } + + static instanceOf(obj: any): obj is HomeserverUnreachable_ { + return obj.tag === FfiImpairment_Tags.HomeserverUnreachable; + } + + } + + type KeepAliveRestartFailing__interface = { + tag: FfiImpairment_Tags.KeepAliveRestartFailing; + inner: +Readonly<{sinceTs: bigint; firesAtTs: bigint; lastError: string}> + }; + class KeepAliveRestartFailing_ extends UniffiEnum implements KeepAliveRestartFailing__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.KeepAliveRestartFailing; + readonly inner: +Readonly<{sinceTs: bigint; firesAtTs: bigint; lastError: string}>; + constructor( +inner: {sinceTs: bigint; firesAtTs: bigint; lastError: string }) { + super("FfiImpairment", "KeepAliveRestartFailing"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {sinceTs: bigint; firesAtTs: bigint; lastError: string }): KeepAliveRestartFailing_ { + return new KeepAliveRestartFailing_(inner); + } + + static instanceOf(obj: any): obj is KeepAliveRestartFailing_ { + return obj.tag === FfiImpairment_Tags.KeepAliveRestartFailing; + } + + } + + type KeepAliveExpired__interface = { + tag: FfiImpairment_Tags.KeepAliveExpired; + inner: +Readonly<{sinceTs: bigint}> + }; + class KeepAliveExpired_ extends UniffiEnum implements KeepAliveExpired__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.KeepAliveExpired; + readonly inner: +Readonly<{sinceTs: bigint}>; + constructor( +inner: {sinceTs: bigint }) { + super("FfiImpairment", "KeepAliveExpired"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {sinceTs: bigint }): KeepAliveExpired_ { + return new KeepAliveExpired_(inner); + } + + static instanceOf(obj: any): obj is KeepAliveExpired_ { + return obj.tag === FfiImpairment_Tags.KeepAliveExpired; + } + + } + + type KeepAliveUnavailable__interface = { + tag: FfiImpairment_Tags.KeepAliveUnavailable; + inner: +Readonly<{permanent: boolean; membershipExpiresAtTs: bigint}> + }; + class KeepAliveUnavailable_ extends UniffiEnum implements KeepAliveUnavailable__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.KeepAliveUnavailable; + readonly inner: +Readonly<{permanent: boolean; membershipExpiresAtTs: bigint}>; + constructor( +inner: {permanent: boolean; membershipExpiresAtTs: bigint }) { + super("FfiImpairment", "KeepAliveUnavailable"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {permanent: boolean; membershipExpiresAtTs: bigint }): KeepAliveUnavailable_ { + return new KeepAliveUnavailable_(inner); + } + + static instanceOf(obj: any): obj is KeepAliveUnavailable_ { + return obj.tag === FfiImpairment_Tags.KeepAliveUnavailable; + } + + } + + type MembershipRefreshFailing__interface = { + tag: FfiImpairment_Tags.MembershipRefreshFailing; + inner: +Readonly<{sinceTs: bigint; expiresAtTs: bigint; lastError: string}> + }; + class MembershipRefreshFailing_ extends UniffiEnum implements MembershipRefreshFailing__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.MembershipRefreshFailing; + readonly inner: +Readonly<{sinceTs: bigint; expiresAtTs: bigint; lastError: string}>; + constructor( +inner: {sinceTs: bigint; expiresAtTs: bigint; lastError: string }) { + super("FfiImpairment", "MembershipRefreshFailing"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {sinceTs: bigint; expiresAtTs: bigint; lastError: string }): MembershipRefreshFailing_ { + return new MembershipRefreshFailing_(inner); + } + + static instanceOf(obj: any): obj is MembershipRefreshFailing_ { + return obj.tag === FfiImpairment_Tags.MembershipRefreshFailing; + } + + } + + type OwnMembershipMissing__interface = { + tag: FfiImpairment_Tags.OwnMembershipMissing; + inner: +Readonly<{sinceTs: bigint; republishedAtTs?: bigint}> + }; + class OwnMembershipMissing_ extends UniffiEnum implements OwnMembershipMissing__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.OwnMembershipMissing; + readonly inner: +Readonly<{sinceTs: bigint; republishedAtTs?: bigint}>; + constructor( +inner: {sinceTs: bigint; republishedAtTs?: bigint }) { + super("FfiImpairment", "OwnMembershipMissing"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {sinceTs: bigint; republishedAtTs?: bigint }): OwnMembershipMissing_ { + return new OwnMembershipMissing_(inner); + } + + static instanceOf(obj: any): obj is OwnMembershipMissing_ { + return obj.tag === FfiImpairment_Tags.OwnMembershipMissing; + } + + } + + type OwnMembershipExcluded__interface = { + tag: FfiImpairment_Tags.OwnMembershipExcluded; + inner: +Readonly<{reason: FfiJoinExclusionReason}> + }; + class OwnMembershipExcluded_ extends UniffiEnum implements OwnMembershipExcluded__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.OwnMembershipExcluded; + readonly inner: +Readonly<{reason: FfiJoinExclusionReason}>; + constructor( +inner: {reason: FfiJoinExclusionReason }) { + super("FfiImpairment", "OwnMembershipExcluded"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {reason: FfiJoinExclusionReason }): OwnMembershipExcluded_ { + return new OwnMembershipExcluded_(inner); + } + + static instanceOf(obj: any): obj is OwnMembershipExcluded_ { + return obj.tag === FfiImpairment_Tags.OwnMembershipExcluded; + } + + } + + type MediaKeyNotDelivered__interface = { + tag: FfiImpairment_Tags.MediaKeyNotDelivered; + inner: +Readonly<{memberIds: Array}> + }; + class MediaKeyNotDelivered_ extends UniffiEnum implements MediaKeyNotDelivered__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.MediaKeyNotDelivered; + readonly inner: +Readonly<{memberIds: Array}>; + constructor( +inner: {memberIds: Array }) { + super("FfiImpairment", "MediaKeyNotDelivered"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {memberIds: Array }): MediaKeyNotDelivered_ { + return new MediaKeyNotDelivered_(inner); + } + + static instanceOf(obj: any): obj is MediaKeyNotDelivered_ { + return obj.tag === FfiImpairment_Tags.MediaKeyNotDelivered; + } + + } + + type MediaKeyNotReceived__interface = { + tag: FfiImpairment_Tags.MediaKeyNotReceived; + inner: +Readonly<{memberIds: Array}> + }; + class MediaKeyNotReceived_ extends UniffiEnum implements MediaKeyNotReceived__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.MediaKeyNotReceived; + readonly inner: +Readonly<{memberIds: Array}>; + constructor( +inner: {memberIds: Array }) { + super("FfiImpairment", "MediaKeyNotReceived"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {memberIds: Array }): MediaKeyNotReceived_ { + return new MediaKeyNotReceived_(inner); + } + + static instanceOf(obj: any): obj is MediaKeyNotReceived_ { + return obj.tag === FfiImpairment_Tags.MediaKeyNotReceived; + } + + } + + type MediaKeyRejected__interface = { + tag: FfiImpairment_Tags.MediaKeyRejected; + inner: +Readonly<{memberId: string; senderUserId: string; reason: FfiKeyRejection; atTs: bigint}> + }; + class MediaKeyRejected_ extends UniffiEnum implements MediaKeyRejected__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.MediaKeyRejected; + readonly inner: +Readonly<{memberId: string; senderUserId: string; reason: FfiKeyRejection; atTs: bigint}>; + constructor( +inner: {memberId: string; senderUserId: string; reason: FfiKeyRejection; atTs: bigint }) { + super("FfiImpairment", "MediaKeyRejected"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {memberId: string; senderUserId: string; reason: FfiKeyRejection; atTs: bigint }): MediaKeyRejected_ { + return new MediaKeyRejected_(inner); + } + + static instanceOf(obj: any): obj is MediaKeyRejected_ { + return obj.tag === FfiImpairment_Tags.MediaKeyRejected; + } + + } + + type ConnectionUnavailable__interface = { + tag: FfiImpairment_Tags.ConnectionUnavailable; + inner: +Readonly<{serviceUrl: string; memberIds: Array; lastError: string; retryAtTs: bigint}> + }; + class ConnectionUnavailable_ extends UniffiEnum implements ConnectionUnavailable__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.ConnectionUnavailable; + readonly inner: +Readonly<{serviceUrl: string; memberIds: Array; lastError: string; retryAtTs: bigint}>; + constructor( +inner: {serviceUrl: string; memberIds: Array; lastError: string; retryAtTs: bigint }) { + super("FfiImpairment", "ConnectionUnavailable"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {serviceUrl: string; memberIds: Array; lastError: string; retryAtTs: bigint }): ConnectionUnavailable_ { + return new ConnectionUnavailable_(inner); + } + + static instanceOf(obj: any): obj is ConnectionUnavailable_ { + return obj.tag === FfiImpairment_Tags.ConnectionUnavailable; + } + + } + + type ConnectionTokenExpired__interface = { + tag: FfiImpairment_Tags.ConnectionTokenExpired; + inner: +Readonly<{serviceUrl: string; expiredAtTs: bigint; lastError: string}> + }; + class ConnectionTokenExpired_ extends UniffiEnum implements ConnectionTokenExpired__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.ConnectionTokenExpired; + readonly inner: +Readonly<{serviceUrl: string; expiredAtTs: bigint; lastError: string}>; + constructor( +inner: {serviceUrl: string; expiredAtTs: bigint; lastError: string }) { + super("FfiImpairment", "ConnectionTokenExpired"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {serviceUrl: string; expiredAtTs: bigint; lastError: string }): ConnectionTokenExpired_ { + return new ConnectionTokenExpired_(inner); + } + + static instanceOf(obj: any): obj is ConnectionTokenExpired_ { + return obj.tag === FfiImpairment_Tags.ConnectionTokenExpired; + } + + } + + type SessionStateUnread__interface = { + tag: FfiImpairment_Tags.SessionStateUnread; + inner: +Readonly<{reads: Array}> + }; + class SessionStateUnread_ extends UniffiEnum implements SessionStateUnread__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.SessionStateUnread; + readonly inner: +Readonly<{reads: Array}>; + constructor( +inner: {reads: Array }) { + super("FfiImpairment", "SessionStateUnread"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {reads: Array }): SessionStateUnread_ { + return new SessionStateUnread_(inner); + } + + static instanceOf(obj: any): obj is SessionStateUnread_ { + return obj.tag === FfiImpairment_Tags.SessionStateUnread; + } + + } + + type JoinedBeforeSeed__interface = { + tag: FfiImpairment_Tags.JoinedBeforeSeed; + inner: +Readonly<{atTs: bigint}> + }; + class JoinedBeforeSeed_ extends UniffiEnum implements JoinedBeforeSeed__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiImpairment"; + readonly tag = FfiImpairment_Tags.JoinedBeforeSeed; + readonly inner: +Readonly<{atTs: bigint}>; + constructor( +inner: {atTs: bigint }) { + super("FfiImpairment", "JoinedBeforeSeed"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {atTs: bigint }): JoinedBeforeSeed_ { + return new JoinedBeforeSeed_(inner); + } + + static instanceOf(obj: any): obj is JoinedBeforeSeed_ { + return obj.tag === FfiImpairment_Tags.JoinedBeforeSeed; + } + + } + + function instanceOf(obj: any): obj is FfiImpairment { + return obj[uniffiTypeNameSymbol] === "FfiImpairment"; + } + + return Object.freeze({ + instanceOf, + HomeserverUnreachable: HomeserverUnreachable_, + KeepAliveRestartFailing: KeepAliveRestartFailing_, + KeepAliveExpired: KeepAliveExpired_, + KeepAliveUnavailable: KeepAliveUnavailable_, + MembershipRefreshFailing: MembershipRefreshFailing_, + OwnMembershipMissing: OwnMembershipMissing_, + OwnMembershipExcluded: OwnMembershipExcluded_, + MediaKeyNotDelivered: MediaKeyNotDelivered_, + MediaKeyNotReceived: MediaKeyNotReceived_, + MediaKeyRejected: MediaKeyRejected_, + ConnectionUnavailable: ConnectionUnavailable_, + ConnectionTokenExpired: ConnectionTokenExpired_, + SessionStateUnread: SessionStateUnread_, + JoinedBeforeSeed: JoinedBeforeSeed_ + }); + +})(); +/** + * A condition that is true right now and that the crate is still working + * on. Every variant clears by itself when the underlying operation + * succeeds — an impairment is never terminal; anything terminal ends the + * participation and appears as [`FfiDisconnectCause`] instead. + * + * A host that renders one warning banner can read this list and nothing + * else; the structured status above is for anything that needs the details. + */ +export type FfiImpairment = InstanceType< + typeof FfiImpairment['HomeserverUnreachable' | 'KeepAliveRestartFailing' | 'KeepAliveExpired' | 'KeepAliveUnavailable' | 'MembershipRefreshFailing' | 'OwnMembershipMissing' | 'OwnMembershipExcluded' | 'MediaKeyNotDelivered' | 'MediaKeyNotReceived' | 'MediaKeyRejected' | 'ConnectionUnavailable' | 'ConnectionTokenExpired' | 'SessionStateUnread' | 'JoinedBeforeSeed'] +>; + +// FfiConverter for enum FfiImpairment +const FfiConverterTypeFfiImpairment = (() => { + type TypeName = FfiImpairment; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new FfiImpairment.HomeserverUnreachable({sinceTs: FfiConverterUInt64.readFromCursor(c) }); + case 2: return new FfiImpairment.KeepAliveRestartFailing({sinceTs: FfiConverterUInt64.readFromCursor(c), firesAtTs: FfiConverterUInt64.readFromCursor(c), lastError: FfiConverterString.readFromCursor(c) }); + case 3: return new FfiImpairment.KeepAliveExpired({sinceTs: FfiConverterUInt64.readFromCursor(c) }); + case 4: return new FfiImpairment.KeepAliveUnavailable({permanent: FfiConverterBool.readFromCursor(c), membershipExpiresAtTs: FfiConverterUInt64.readFromCursor(c) }); + case 5: return new FfiImpairment.MembershipRefreshFailing({sinceTs: FfiConverterUInt64.readFromCursor(c), expiresAtTs: FfiConverterUInt64.readFromCursor(c), lastError: FfiConverterString.readFromCursor(c) }); + case 6: return new FfiImpairment.OwnMembershipMissing({sinceTs: FfiConverterUInt64.readFromCursor(c), republishedAtTs: FfiConverterOptionalUInt64.readFromCursor(c) }); + case 7: return new FfiImpairment.OwnMembershipExcluded({reason: FfiConverterTypeFfiJoinExclusionReason.readFromCursor(c) }); + case 8: return new FfiImpairment.MediaKeyNotDelivered({memberIds: FfiConverterSequenceString.readFromCursor(c) }); + case 9: return new FfiImpairment.MediaKeyNotReceived({memberIds: FfiConverterSequenceString.readFromCursor(c) }); + case 10: return new FfiImpairment.MediaKeyRejected({memberId: FfiConverterString.readFromCursor(c), senderUserId: FfiConverterString.readFromCursor(c), reason: FfiConverterTypeFfiKeyRejection.readFromCursor(c), atTs: FfiConverterUInt64.readFromCursor(c) }); + case 11: return new FfiImpairment.ConnectionUnavailable({serviceUrl: FfiConverterString.readFromCursor(c), memberIds: FfiConverterSequenceString.readFromCursor(c), lastError: FfiConverterString.readFromCursor(c), retryAtTs: FfiConverterUInt64.readFromCursor(c) }); + case 12: return new FfiImpairment.ConnectionTokenExpired({serviceUrl: FfiConverterString.readFromCursor(c), expiredAtTs: FfiConverterUInt64.readFromCursor(c), lastError: FfiConverterString.readFromCursor(c) }); + case 13: return new FfiImpairment.SessionStateUnread({reads: FfiConverterSequenceTypeFfiSessionRead.readFromCursor(c) }); + case 14: return new FfiImpairment.JoinedBeforeSeed({atTs: FfiConverterUInt64.readFromCursor(c) }); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case FfiImpairment_Tags.HomeserverUnreachable: { + c.writeI32(1); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.sinceTs, c); + return; + } + case FfiImpairment_Tags.KeepAliveRestartFailing: { + c.writeI32(2); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.sinceTs, c); + FfiConverterUInt64.writeIntoCursor(inner.firesAtTs, c); + FfiConverterString.writeIntoCursor(inner.lastError, c); + return; + } + case FfiImpairment_Tags.KeepAliveExpired: { + c.writeI32(3); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.sinceTs, c); + return; + } + case FfiImpairment_Tags.KeepAliveUnavailable: { + c.writeI32(4); + const inner = value.inner; + FfiConverterBool.writeIntoCursor(inner.permanent, c); + FfiConverterUInt64.writeIntoCursor(inner.membershipExpiresAtTs, c); + return; + } + case FfiImpairment_Tags.MembershipRefreshFailing: { + c.writeI32(5); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.sinceTs, c); + FfiConverterUInt64.writeIntoCursor(inner.expiresAtTs, c); + FfiConverterString.writeIntoCursor(inner.lastError, c); + return; + } + case FfiImpairment_Tags.OwnMembershipMissing: { + c.writeI32(6); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.sinceTs, c); + FfiConverterOptionalUInt64.writeIntoCursor(inner.republishedAtTs, c); + return; + } + case FfiImpairment_Tags.OwnMembershipExcluded: { + c.writeI32(7); + const inner = value.inner; + FfiConverterTypeFfiJoinExclusionReason.writeIntoCursor(inner.reason, c); + return; + } + case FfiImpairment_Tags.MediaKeyNotDelivered: { + c.writeI32(8); + const inner = value.inner; + FfiConverterSequenceString.writeIntoCursor(inner.memberIds, c); + return; + } + case FfiImpairment_Tags.MediaKeyNotReceived: { + c.writeI32(9); + const inner = value.inner; + FfiConverterSequenceString.writeIntoCursor(inner.memberIds, c); + return; + } + case FfiImpairment_Tags.MediaKeyRejected: { + c.writeI32(10); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner.memberId, c); + FfiConverterString.writeIntoCursor(inner.senderUserId, c); + FfiConverterTypeFfiKeyRejection.writeIntoCursor(inner.reason, c); + FfiConverterUInt64.writeIntoCursor(inner.atTs, c); + return; + } + case FfiImpairment_Tags.ConnectionUnavailable: { + c.writeI32(11); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner.serviceUrl, c); + FfiConverterSequenceString.writeIntoCursor(inner.memberIds, c); + FfiConverterString.writeIntoCursor(inner.lastError, c); + FfiConverterUInt64.writeIntoCursor(inner.retryAtTs, c); + return; + } + case FfiImpairment_Tags.ConnectionTokenExpired: { + c.writeI32(12); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner.serviceUrl, c); + FfiConverterUInt64.writeIntoCursor(inner.expiredAtTs, c); + FfiConverterString.writeIntoCursor(inner.lastError, c); + return; + } + case FfiImpairment_Tags.SessionStateUnread: { + c.writeI32(13); + const inner = value.inner; + FfiConverterSequenceTypeFfiSessionRead.writeIntoCursor(inner.reads, c); + return; + } + case FfiImpairment_Tags.JoinedBeforeSeed: { + c.writeI32(14); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.atTs, c); + return; + } + default: + // Throwing from here means that FfiImpairment_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case FfiImpairment_Tags.HomeserverUnreachable: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.sinceTs); + return size; + } + case FfiImpairment_Tags.KeepAliveRestartFailing: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.sinceTs); + size += FfiConverterUInt64.allocationSize(inner.firesAtTs); + size += FfiConverterString.allocationSize(inner.lastError); + return size; + } + case FfiImpairment_Tags.KeepAliveExpired: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.sinceTs); + return size; + } + case FfiImpairment_Tags.KeepAliveUnavailable: { + const inner = value.inner; + let size = 4; + size += FfiConverterBool.allocationSize(inner.permanent); + size += FfiConverterUInt64.allocationSize(inner.membershipExpiresAtTs); + return size; + } + case FfiImpairment_Tags.MembershipRefreshFailing: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.sinceTs); + size += FfiConverterUInt64.allocationSize(inner.expiresAtTs); + size += FfiConverterString.allocationSize(inner.lastError); + return size; + } + case FfiImpairment_Tags.OwnMembershipMissing: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.sinceTs); + size += FfiConverterOptionalUInt64.allocationSize(inner.republishedAtTs); + return size; + } + case FfiImpairment_Tags.OwnMembershipExcluded: { + const inner = value.inner; + let size = 4; + size += FfiConverterTypeFfiJoinExclusionReason.allocationSize(inner.reason); + return size; + } + case FfiImpairment_Tags.MediaKeyNotDelivered: { + const inner = value.inner; + let size = 4; + size += FfiConverterSequenceString.allocationSize(inner.memberIds); + return size; + } + case FfiImpairment_Tags.MediaKeyNotReceived: { + const inner = value.inner; + let size = 4; + size += FfiConverterSequenceString.allocationSize(inner.memberIds); + return size; + } + case FfiImpairment_Tags.MediaKeyRejected: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner.memberId); + size += FfiConverterString.allocationSize(inner.senderUserId); + size += FfiConverterTypeFfiKeyRejection.allocationSize(inner.reason); + size += FfiConverterUInt64.allocationSize(inner.atTs); + return size; + } + case FfiImpairment_Tags.ConnectionUnavailable: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner.serviceUrl); + size += FfiConverterSequenceString.allocationSize(inner.memberIds); + size += FfiConverterString.allocationSize(inner.lastError); + size += FfiConverterUInt64.allocationSize(inner.retryAtTs); + return size; + } + case FfiImpairment_Tags.ConnectionTokenExpired: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner.serviceUrl); + size += FfiConverterUInt64.allocationSize(inner.expiredAtTs); + size += FfiConverterString.allocationSize(inner.lastError); + return size; + } + case FfiImpairment_Tags.SessionStateUnread: { + const inner = value.inner; + let size = 4; + size += FfiConverterSequenceTypeFfiSessionRead.allocationSize(inner.reads); + return size; + } + case FfiImpairment_Tags.JoinedBeforeSeed: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.atTs); + return size; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + + +// Enum: FfiKeepAlive +export enum FfiKeepAlive_Tags { + Armed = "Armed", + Delegated = "Delegated", + RestartFailing = "RestartFailing", + Expired = "Expired", + Unavailable = "Unavailable" +} +/** + * The dead man's switch that clears our membership if this client dies. + * Mutually exclusive states of one mechanism. + */ +export const FfiKeepAlive = (() => { + + type Armed__interface = { + tag: FfiKeepAlive_Tags.Armed; + inner: +Readonly<{delayMs: bigint; lastRestartTs: bigint; firesAtTs: bigint}> + }; + /** + * Armed, and we restart it ourselves. + */ + class Armed_ extends UniffiEnum implements Armed__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiKeepAlive"; + readonly tag = FfiKeepAlive_Tags.Armed; + readonly inner: +Readonly<{delayMs: bigint; lastRestartTs: bigint; firesAtTs: bigint}>; + constructor( +inner: {delayMs: bigint; lastRestartTs: bigint; firesAtTs: bigint }) { + super("FfiKeepAlive", "Armed"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {delayMs: bigint; lastRestartTs: bigint; firesAtTs: bigint }): Armed_ { + return new Armed_(inner); + } + + static instanceOf(obj: any): obj is Armed_ { + return obj.tag === FfiKeepAlive_Tags.Armed; + } + + } + + type Delegated__interface = { + tag: FfiKeepAlive_Tags.Delegated; + inner: +Readonly<{delegatedAtTs: bigint; earliestFireTs: bigint}> + }; + /** + * Handed to the SFU (MSC4195): we no longer restart it, so a frozen + * `last_restart_ts` is expected here rather than a fault. + */ + class Delegated_ extends UniffiEnum implements Delegated__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiKeepAlive"; + readonly tag = FfiKeepAlive_Tags.Delegated; + readonly inner: +Readonly<{delegatedAtTs: bigint; earliestFireTs: bigint}>; + constructor( +inner: {delegatedAtTs: bigint; earliestFireTs: bigint }) { + super("FfiKeepAlive", "Delegated"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {delegatedAtTs: bigint; earliestFireTs: bigint }): Delegated_ { + return new Delegated_(inner); + } + + static instanceOf(obj: any): obj is Delegated_ { + return obj.tag === FfiKeepAlive_Tags.Delegated; + } + + } + + type RestartFailing__interface = { + tag: FfiKeepAlive_Tags.RestartFailing; + inner: +Readonly<{sinceTs: bigint; firesAtTs: bigint; lastError: string}> + }; + /** + * Armed, but restarts are failing — we drop out at `fires_at_ts` + * unless one succeeds. + */ + class RestartFailing_ extends UniffiEnum implements RestartFailing__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiKeepAlive"; + readonly tag = FfiKeepAlive_Tags.RestartFailing; + readonly inner: +Readonly<{sinceTs: bigint; firesAtTs: bigint; lastError: string}>; + constructor( +inner: {sinceTs: bigint; firesAtTs: bigint; lastError: string }) { + super("FfiKeepAlive", "RestartFailing"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {sinceTs: bigint; firesAtTs: bigint; lastError: string }): RestartFailing_ { + return new RestartFailing_(inner); + } + + static instanceOf(obj: any): obj is RestartFailing_ { + return obj.tag === FfiKeepAlive_Tags.RestartFailing; + } + + } + + type Expired__interface = { + tag: FfiKeepAlive_Tags.Expired; + inner: +Readonly<{sinceTs: bigint}> + }; + /** + * Its delay elapsed with no successful restart: we are probably out + * already. A replacement is being armed. + */ + class Expired_ extends UniffiEnum implements Expired__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiKeepAlive"; + readonly tag = FfiKeepAlive_Tags.Expired; + readonly inner: +Readonly<{sinceTs: bigint}>; + constructor( +inner: {sinceTs: bigint }) { + super("FfiKeepAlive", "Expired"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {sinceTs: bigint }): Expired_ { + return new Expired_(inner); + } + + static instanceOf(obj: any): obj is Expired_ { + return obj.tag === FfiKeepAlive_Tags.Expired; + } + + } + + type Unavailable__interface = { + tag: FfiKeepAlive_Tags.Unavailable; + inner: +Readonly<{permanent: boolean; nextProbeTs?: bigint}> + }; + /** + * None armed. `permanent` = this homeserver refuses delayed events for + * good; otherwise we re-probe at `next_probe_ts` (`Some(0)` = next beat). + */ + class Unavailable_ extends UniffiEnum implements Unavailable__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiKeepAlive"; + readonly tag = FfiKeepAlive_Tags.Unavailable; + readonly inner: +Readonly<{permanent: boolean; nextProbeTs?: bigint}>; + constructor( +inner: {permanent: boolean; nextProbeTs?: bigint }) { + super("FfiKeepAlive", "Unavailable"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {permanent: boolean; nextProbeTs?: bigint }): Unavailable_ { + return new Unavailable_(inner); + } + + static instanceOf(obj: any): obj is Unavailable_ { + return obj.tag === FfiKeepAlive_Tags.Unavailable; + } + + } + + function instanceOf(obj: any): obj is FfiKeepAlive { + return obj[uniffiTypeNameSymbol] === "FfiKeepAlive"; + } + + return Object.freeze({ + instanceOf, + Armed: Armed_, + Delegated: Delegated_, + RestartFailing: RestartFailing_, + Expired: Expired_, + Unavailable: Unavailable_ + }); + +})(); +/** + * The dead man's switch that clears our membership if this client dies. + * Mutually exclusive states of one mechanism. + */ +export type FfiKeepAlive = InstanceType< + typeof FfiKeepAlive['Armed' | 'Delegated' | 'RestartFailing' | 'Expired' | 'Unavailable'] +>; + +// FfiConverter for enum FfiKeepAlive +const FfiConverterTypeFfiKeepAlive = (() => { + type TypeName = FfiKeepAlive; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new FfiKeepAlive.Armed({delayMs: FfiConverterUInt64.readFromCursor(c), lastRestartTs: FfiConverterUInt64.readFromCursor(c), firesAtTs: FfiConverterUInt64.readFromCursor(c) }); + case 2: return new FfiKeepAlive.Delegated({delegatedAtTs: FfiConverterUInt64.readFromCursor(c), earliestFireTs: FfiConverterUInt64.readFromCursor(c) }); + case 3: return new FfiKeepAlive.RestartFailing({sinceTs: FfiConverterUInt64.readFromCursor(c), firesAtTs: FfiConverterUInt64.readFromCursor(c), lastError: FfiConverterString.readFromCursor(c) }); + case 4: return new FfiKeepAlive.Expired({sinceTs: FfiConverterUInt64.readFromCursor(c) }); + case 5: return new FfiKeepAlive.Unavailable({permanent: FfiConverterBool.readFromCursor(c), nextProbeTs: FfiConverterOptionalUInt64.readFromCursor(c) }); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case FfiKeepAlive_Tags.Armed: { + c.writeI32(1); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.delayMs, c); + FfiConverterUInt64.writeIntoCursor(inner.lastRestartTs, c); + FfiConverterUInt64.writeIntoCursor(inner.firesAtTs, c); + return; + } + case FfiKeepAlive_Tags.Delegated: { + c.writeI32(2); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.delegatedAtTs, c); + FfiConverterUInt64.writeIntoCursor(inner.earliestFireTs, c); + return; + } + case FfiKeepAlive_Tags.RestartFailing: { + c.writeI32(3); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.sinceTs, c); + FfiConverterUInt64.writeIntoCursor(inner.firesAtTs, c); + FfiConverterString.writeIntoCursor(inner.lastError, c); + return; + } + case FfiKeepAlive_Tags.Expired: { + c.writeI32(4); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.sinceTs, c); + return; + } + case FfiKeepAlive_Tags.Unavailable: { + c.writeI32(5); + const inner = value.inner; + FfiConverterBool.writeIntoCursor(inner.permanent, c); + FfiConverterOptionalUInt64.writeIntoCursor(inner.nextProbeTs, c); + return; + } + default: + // Throwing from here means that FfiKeepAlive_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case FfiKeepAlive_Tags.Armed: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.delayMs); + size += FfiConverterUInt64.allocationSize(inner.lastRestartTs); + size += FfiConverterUInt64.allocationSize(inner.firesAtTs); + return size; + } + case FfiKeepAlive_Tags.Delegated: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.delegatedAtTs); + size += FfiConverterUInt64.allocationSize(inner.earliestFireTs); + return size; + } + case FfiKeepAlive_Tags.RestartFailing: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.sinceTs); + size += FfiConverterUInt64.allocationSize(inner.firesAtTs); + size += FfiConverterString.allocationSize(inner.lastError); + return size; + } + case FfiKeepAlive_Tags.Expired: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.sinceTs); + return size; + } + case FfiKeepAlive_Tags.Unavailable: { + const inner = value.inner; + let size = 4; + size += FfiConverterBool.allocationSize(inner.permanent); + size += FfiConverterOptionalUInt64.allocationSize(inner.nextProbeTs); + return size; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + + +// Enum: FfiRosterPresence +export enum FfiRosterPresence_Tags { + AwaitingEcho = "AwaitingEcho", + Present = "Present", + Missing = "Missing", + Excluded = "Excluded" +} +/** + * Whether the session projects our own membership — i.e. whether anybody + * can see us. + */ +export const FfiRosterPresence = (() => { + + type AwaitingEcho__interface = { + tag: FfiRosterPresence_Tags.AwaitingEcho + }; + /** + * Sent, echo not back yet. Not a fault. + */ + class AwaitingEcho_ extends UniffiEnum implements AwaitingEcho__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiRosterPresence"; + readonly tag = FfiRosterPresence_Tags.AwaitingEcho; + constructor() { + super("FfiRosterPresence", "AwaitingEcho"); + } + + static new(): AwaitingEcho_ { + return new AwaitingEcho_(); + } + + static instanceOf(obj: any): obj is AwaitingEcho_ { + return obj.tag === FfiRosterPresence_Tags.AwaitingEcho; + } + + } + + type Present__interface = { + tag: FfiRosterPresence_Tags.Present + }; + class Present_ extends UniffiEnum implements Present__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiRosterPresence"; + readonly tag = FfiRosterPresence_Tags.Present; + constructor() { + super("FfiRosterPresence", "Present"); + } + + static new(): Present_ { + return new Present_(); + } + + static instanceOf(obj: any): obj is Present_ { + return obj.tag === FfiRosterPresence_Tags.Present; + } + + } + + type Missing__interface = { + tag: FfiRosterPresence_Tags.Missing; + inner: +Readonly<{sinceTs: bigint; republishedAtTs?: bigint}> + }; + /** + * It was in the roster and is gone. + */ + class Missing_ extends UniffiEnum implements Missing__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiRosterPresence"; + readonly tag = FfiRosterPresence_Tags.Missing; + readonly inner: +Readonly<{sinceTs: bigint; republishedAtTs?: bigint}>; + constructor( +inner: {sinceTs: bigint; republishedAtTs?: bigint }) { + super("FfiRosterPresence", "Missing"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {sinceTs: bigint; republishedAtTs?: bigint }): Missing_ { + return new Missing_(inner); + } + + static instanceOf(obj: any): obj is Missing_ { + return obj.tag === FfiRosterPresence_Tags.Missing; + } + + } + + type Excluded__interface = { + tag: FfiRosterPresence_Tags.Excluded; + inner: +Readonly<{reason: FfiJoinExclusionReason}> + }; + /** + * On the server, but the session refuses to project it. The self-heal + * deliberately does not re-send here. + */ + class Excluded_ extends UniffiEnum implements Excluded__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiRosterPresence"; + readonly tag = FfiRosterPresence_Tags.Excluded; + readonly inner: +Readonly<{reason: FfiJoinExclusionReason}>; + constructor( +inner: {reason: FfiJoinExclusionReason }) { + super("FfiRosterPresence", "Excluded"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {reason: FfiJoinExclusionReason }): Excluded_ { + return new Excluded_(inner); + } + + static instanceOf(obj: any): obj is Excluded_ { + return obj.tag === FfiRosterPresence_Tags.Excluded; + } + + } + + function instanceOf(obj: any): obj is FfiRosterPresence { + return obj[uniffiTypeNameSymbol] === "FfiRosterPresence"; + } + + return Object.freeze({ + instanceOf, + AwaitingEcho: AwaitingEcho_, + Present: Present_, + Missing: Missing_, + Excluded: Excluded_ + }); + +})(); +/** + * Whether the session projects our own membership — i.e. whether anybody + * can see us. + */ +export type FfiRosterPresence = InstanceType< + typeof FfiRosterPresence['AwaitingEcho' | 'Present' | 'Missing' | 'Excluded'] +>; + +// FfiConverter for enum FfiRosterPresence +const FfiConverterTypeFfiRosterPresence = (() => { + type TypeName = FfiRosterPresence; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new FfiRosterPresence.AwaitingEcho(); + case 2: return new FfiRosterPresence.Present(); + case 3: return new FfiRosterPresence.Missing({sinceTs: FfiConverterUInt64.readFromCursor(c), republishedAtTs: FfiConverterOptionalUInt64.readFromCursor(c) }); + case 4: return new FfiRosterPresence.Excluded({reason: FfiConverterTypeFfiJoinExclusionReason.readFromCursor(c) }); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case FfiRosterPresence_Tags.AwaitingEcho: { + c.writeI32(1); + return; + } + case FfiRosterPresence_Tags.Present: { + c.writeI32(2); + return; + } + case FfiRosterPresence_Tags.Missing: { + c.writeI32(3); + const inner = value.inner; + FfiConverterUInt64.writeIntoCursor(inner.sinceTs, c); + FfiConverterOptionalUInt64.writeIntoCursor(inner.republishedAtTs, c); + return; + } + case FfiRosterPresence_Tags.Excluded: { + c.writeI32(4); + const inner = value.inner; + FfiConverterTypeFfiJoinExclusionReason.writeIntoCursor(inner.reason, c); + return; + } + default: + // Throwing from here means that FfiRosterPresence_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case FfiRosterPresence_Tags.AwaitingEcho: { + return 4; + } + case FfiRosterPresence_Tags.Present: { + return 4; + } + case FfiRosterPresence_Tags.Missing: { + const inner = value.inner; + let size = 4; + size += FfiConverterUInt64.allocationSize(inner.sinceTs); + size += FfiConverterOptionalUInt64.allocationSize(inner.republishedAtTs); + return size; + } + case FfiRosterPresence_Tags.Excluded: { + const inner = value.inner; + let size = 4; + size += FfiConverterTypeFfiJoinExclusionReason.allocationSize(inner.reason); + return size; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + +/** + * How severe an [`FfiImpairment`] is, and therefore where to render it. + */ +export enum FfiSeverity { + /** + * We are, or are about to be, out of the call — or peers cannot use + * our media. + */ + Critical, + /** + * Degraded but functioning; a crash or a timeout would now hurt. + */ + Degraded, + /** + * Worth surfacing in diagnostics, not in the call UI. + */ + Notice +} + +const FfiConverterTypeFfiSeverity = (() => { + type TypeName = FfiSeverity; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return FfiSeverity.Critical; + case 2: return FfiSeverity.Degraded; + case 3: return FfiSeverity.Notice; + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value) { + case FfiSeverity.Critical: return c.writeI32(1); + case FfiSeverity.Degraded: return c.writeI32(2); + case FfiSeverity.Notice: return c.writeI32(3); + } + } + allocationSize(value: TypeName): number { + return 4; + } + } + return new FFIConverter(); +})(); + + +// Enum: FfiStatus +export enum FfiStatus_Tags { + Disconnected = "Disconnected", + Joining = "Joining", + Connected = "Connected", + Leaving = "Leaving" +} +/** + * The participation status, in full. + * + * This used to be four opaque variants with everything else hidden behind + * `debug_snapshot`'s unversioned JSON — which is a diagnostics dump, not a + * UI contract. Every field a host needs is now typed. + */ +export const FfiStatus = (() => { + + type Disconnected__interface = { + tag: FfiStatus_Tags.Disconnected; + inner: +Readonly<{cause: FfiDisconnectCause}> + }; + class Disconnected_ extends UniffiEnum implements Disconnected__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiStatus"; + readonly tag = FfiStatus_Tags.Disconnected; + readonly inner: +Readonly<{cause: FfiDisconnectCause}>; + constructor( +inner: {cause: FfiDisconnectCause }) { + super("FfiStatus", "Disconnected"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {cause: FfiDisconnectCause }): Disconnected_ { + return new Disconnected_(inner); + } + + static instanceOf(obj: any): obj is Disconnected_ { + return obj.tag === FfiStatus_Tags.Disconnected; + } + + } + + type Joining__interface = { + tag: FfiStatus_Tags.Joining; + inner: +Readonly<{ownMembership: FfiJoinProgress; encryption: FfiEncryptionStatus; impairments: Array}> + }; + class Joining_ extends UniffiEnum implements Joining__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiStatus"; + readonly tag = FfiStatus_Tags.Joining; + readonly inner: +Readonly<{ownMembership: FfiJoinProgress; encryption: FfiEncryptionStatus; impairments: Array}>; + constructor( +inner: {ownMembership: FfiJoinProgress; encryption: FfiEncryptionStatus; impairments: Array }) { + super("FfiStatus", "Joining"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {ownMembership: FfiJoinProgress; encryption: FfiEncryptionStatus; impairments: Array }): Joining_ { + return new Joining_(inner); + } + + static instanceOf(obj: any): obj is Joining_ { + return obj.tag === FfiStatus_Tags.Joining; + } + + } + + type Connected__interface = { + tag: FfiStatus_Tags.Connected; + inner: +Readonly<{keepAlive: FfiKeepAlive; membership: FfiMembershipPublication; roster: FfiRosterPresence; encryption: FfiEncryptionStatus; impairments: Array}> + }; + class Connected_ extends UniffiEnum implements Connected__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiStatus"; + readonly tag = FfiStatus_Tags.Connected; + readonly inner: +Readonly<{keepAlive: FfiKeepAlive; membership: FfiMembershipPublication; roster: FfiRosterPresence; encryption: FfiEncryptionStatus; impairments: Array}>; + constructor( +inner: {keepAlive: FfiKeepAlive; membership: FfiMembershipPublication; roster: FfiRosterPresence; encryption: FfiEncryptionStatus; impairments: Array }) { + super("FfiStatus", "Connected"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {keepAlive: FfiKeepAlive; membership: FfiMembershipPublication; roster: FfiRosterPresence; encryption: FfiEncryptionStatus; impairments: Array }): Connected_ { + return new Connected_(inner); + } + + static instanceOf(obj: any): obj is Connected_ { + return obj.tag === FfiStatus_Tags.Connected; + } + + } + + type Leaving__interface = { + tag: FfiStatus_Tags.Leaving; + inner: +Readonly<{leaveEventSent: boolean; delayedLeave?: FfiDelayedLeaveOutcome; impairments: Array}> + }; + class Leaving_ extends UniffiEnum implements Leaving__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiStatus"; + readonly tag = FfiStatus_Tags.Leaving; + readonly inner: +Readonly<{leaveEventSent: boolean; delayedLeave?: FfiDelayedLeaveOutcome; impairments: Array}>; + constructor( +inner: {leaveEventSent: boolean; delayedLeave?: FfiDelayedLeaveOutcome; impairments: Array }) { + super("FfiStatus", "Leaving"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {leaveEventSent: boolean; delayedLeave?: FfiDelayedLeaveOutcome; impairments: Array }): Leaving_ { + return new Leaving_(inner); + } + + static instanceOf(obj: any): obj is Leaving_ { + return obj.tag === FfiStatus_Tags.Leaving; + } + + } + + function instanceOf(obj: any): obj is FfiStatus { + return obj[uniffiTypeNameSymbol] === "FfiStatus"; + } + + return Object.freeze({ + instanceOf, + Disconnected: Disconnected_, + Joining: Joining_, + Connected: Connected_, + Leaving: Leaving_ + }); + +})(); +/** + * The participation status, in full. + * + * This used to be four opaque variants with everything else hidden behind + * `debug_snapshot`'s unversioned JSON — which is a diagnostics dump, not a + * UI contract. Every field a host needs is now typed. + */ +export type FfiStatus = InstanceType< + typeof FfiStatus['Disconnected' | 'Joining' | 'Connected' | 'Leaving'] +>; + +// FfiConverter for enum FfiStatus +const FfiConverterTypeFfiStatus = (() => { + type TypeName = FfiStatus; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new FfiStatus.Disconnected({cause: FfiConverterTypeFfiDisconnectCause.readFromCursor(c) }); + case 2: return new FfiStatus.Joining({ownMembership: FfiConverterTypeFfiJoinProgress.readFromCursor(c), encryption: FfiConverterTypeFfiEncryptionStatus.readFromCursor(c), impairments: FfiConverterSequenceTypeFfiImpairment.readFromCursor(c) }); + case 3: return new FfiStatus.Connected({keepAlive: FfiConverterTypeFfiKeepAlive.readFromCursor(c), membership: FfiConverterTypeFfiMembershipPublication.readFromCursor(c), roster: FfiConverterTypeFfiRosterPresence.readFromCursor(c), encryption: FfiConverterTypeFfiEncryptionStatus.readFromCursor(c), impairments: FfiConverterSequenceTypeFfiImpairment.readFromCursor(c) }); + case 4: return new FfiStatus.Leaving({leaveEventSent: FfiConverterBool.readFromCursor(c), delayedLeave: FfiConverterOptionalTypeFfiDelayedLeaveOutcome.readFromCursor(c), impairments: FfiConverterSequenceTypeFfiImpairment.readFromCursor(c) }); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case FfiStatus_Tags.Disconnected: { + c.writeI32(1); + const inner = value.inner; + FfiConverterTypeFfiDisconnectCause.writeIntoCursor(inner.cause, c); + return; + } + case FfiStatus_Tags.Joining: { + c.writeI32(2); + const inner = value.inner; + FfiConverterTypeFfiJoinProgress.writeIntoCursor(inner.ownMembership, c); + FfiConverterTypeFfiEncryptionStatus.writeIntoCursor(inner.encryption, c); + FfiConverterSequenceTypeFfiImpairment.writeIntoCursor(inner.impairments, c); + return; + } + case FfiStatus_Tags.Connected: { + c.writeI32(3); + const inner = value.inner; + FfiConverterTypeFfiKeepAlive.writeIntoCursor(inner.keepAlive, c); + FfiConverterTypeFfiMembershipPublication.writeIntoCursor(inner.membership, c); + FfiConverterTypeFfiRosterPresence.writeIntoCursor(inner.roster, c); + FfiConverterTypeFfiEncryptionStatus.writeIntoCursor(inner.encryption, c); + FfiConverterSequenceTypeFfiImpairment.writeIntoCursor(inner.impairments, c); + return; + } + case FfiStatus_Tags.Leaving: { + c.writeI32(4); + const inner = value.inner; + FfiConverterBool.writeIntoCursor(inner.leaveEventSent, c); + FfiConverterOptionalTypeFfiDelayedLeaveOutcome.writeIntoCursor(inner.delayedLeave, c); + FfiConverterSequenceTypeFfiImpairment.writeIntoCursor(inner.impairments, c); + return; + } + default: + // Throwing from here means that FfiStatus_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case FfiStatus_Tags.Disconnected: { + const inner = value.inner; + let size = 4; + size += FfiConverterTypeFfiDisconnectCause.allocationSize(inner.cause); + return size; + } + case FfiStatus_Tags.Joining: { + const inner = value.inner; + let size = 4; + size += FfiConverterTypeFfiJoinProgress.allocationSize(inner.ownMembership); + size += FfiConverterTypeFfiEncryptionStatus.allocationSize(inner.encryption); + size += FfiConverterSequenceTypeFfiImpairment.allocationSize(inner.impairments); + return size; + } + case FfiStatus_Tags.Connected: { + const inner = value.inner; + let size = 4; + size += FfiConverterTypeFfiKeepAlive.allocationSize(inner.keepAlive); + size += FfiConverterTypeFfiMembershipPublication.allocationSize(inner.membership); + size += FfiConverterTypeFfiRosterPresence.allocationSize(inner.roster); + size += FfiConverterTypeFfiEncryptionStatus.allocationSize(inner.encryption); + size += FfiConverterSequenceTypeFfiImpairment.allocationSize(inner.impairments); + return size; + } + case FfiStatus_Tags.Leaving: { + const inner = value.inner; + let size = 4; + size += FfiConverterBool.allocationSize(inner.leaveEventSent); + size += FfiConverterOptionalTypeFfiDelayedLeaveOutcome.allocationSize(inner.delayedLeave); + size += FfiConverterSequenceTypeFfiImpairment.allocationSize(inner.impairments); + return size; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + + +// Enum: FfiTransportIntent +export enum FfiTransportIntent_Tags { + Publish = "Publish", + ReceiveOnly = "ReceiveOnly" +} +export const FfiTransportIntent = (() => { + + type Publish__interface = { + tag: FfiTransportIntent_Tags.Publish; + inner: +Readonly<{transport: FfiRtcTransport}> + }; + class Publish_ extends UniffiEnum implements Publish__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiTransportIntent"; + readonly tag = FfiTransportIntent_Tags.Publish; + readonly inner: +Readonly<{transport: FfiRtcTransport}>; + constructor( +inner: {transport: FfiRtcTransport }) { + super("FfiTransportIntent", "Publish"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {transport: FfiRtcTransport }): Publish_ { + return new Publish_(inner); + } + + static instanceOf(obj: any): obj is Publish_ { + return obj.tag === FfiTransportIntent_Tags.Publish; + } + + } + + type ReceiveOnly__interface = { + tag: FfiTransportIntent_Tags.ReceiveOnly; + inner: +Readonly<{canSubscribe: Array}> + }; + class ReceiveOnly_ extends UniffiEnum implements ReceiveOnly__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "FfiTransportIntent"; + readonly tag = FfiTransportIntent_Tags.ReceiveOnly; + readonly inner: +Readonly<{canSubscribe: Array}>; + constructor( +inner: {canSubscribe: Array }) { + super("FfiTransportIntent", "ReceiveOnly"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {canSubscribe: Array }): ReceiveOnly_ { + return new ReceiveOnly_(inner); + } + + static instanceOf(obj: any): obj is ReceiveOnly_ { + return obj.tag === FfiTransportIntent_Tags.ReceiveOnly; + } + + } + + function instanceOf(obj: any): obj is FfiTransportIntent { + return obj[uniffiTypeNameSymbol] === "FfiTransportIntent"; + } + + return Object.freeze({ + instanceOf, + Publish: Publish_, + ReceiveOnly: ReceiveOnly_ + }); + +})(); +export type FfiTransportIntent = InstanceType< + typeof FfiTransportIntent['Publish' | 'ReceiveOnly'] +>; + +// FfiConverter for enum FfiTransportIntent +const FfiConverterTypeFfiTransportIntent = (() => { + type TypeName = FfiTransportIntent; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new FfiTransportIntent.Publish({transport: FfiConverterTypeFfiRtcTransport.readFromCursor(c) }); + case 2: return new FfiTransportIntent.ReceiveOnly({canSubscribe: FfiConverterSequenceString.readFromCursor(c) }); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case FfiTransportIntent_Tags.Publish: { + c.writeI32(1); + const inner = value.inner; + FfiConverterTypeFfiRtcTransport.writeIntoCursor(inner.transport, c); + return; + } + case FfiTransportIntent_Tags.ReceiveOnly: { + c.writeI32(2); + const inner = value.inner; + FfiConverterSequenceString.writeIntoCursor(inner.canSubscribe, c); + return; + } + default: + // Throwing from here means that FfiTransportIntent_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case FfiTransportIntent_Tags.Publish: { + const inner = value.inner; + let size = 4; + size += FfiConverterTypeFfiRtcTransport.allocationSize(inner.transport); + return size; + } + case FfiTransportIntent_Tags.ReceiveOnly: { + const inner = value.inner; + let size = 4; + size += FfiConverterSequenceString.allocationSize(inner.canSubscribe); + return size; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + + +// Error type: RtcError +export enum RtcError_Tags { + InvalidInput = "InvalidInput", + Http = "Http", + Driver = "Driver", + Rejected = "Rejected", + Unsupported = "Unsupported", + RateLimited = "RateLimited", + Stopped = "Stopped", + AlreadyJoined = "AlreadyJoined", + NotJoined = "NotJoined", + SlotClosed = "SlotClosed", + NoTransport = "NoTransport", + TokenRefused = "TokenRefused", + EncryptionSetup = "EncryptionSetup" +} +/** + * Errors across the FFI, both directions. A foreign driver maps its + * homeserver errors onto these: `Rejected` = 403 `M_FORBIDDEN`, + * `Unsupported` = 404 `M_UNRECOGNIZED` — both read as "this homeserver will + * never do delayed events" by the own-membership machine. + */ +export const RtcError = (() => { + + type InvalidInput__interface = { + tag: RtcError_Tags.InvalidInput; + inner: +Readonly< +[string +]> + }; + class InvalidInput_ extends UniffiError implements InvalidInput__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.InvalidInput; + readonly inner: +Readonly< +[string +]>; + constructor(v0: string) { + super("RtcError", "InvalidInput"); + + this.inner = Object.freeze([v0]); + } + static new(v0: string): InvalidInput_ { + return new InvalidInput_(v0); + } + + static instanceOf(obj: any): obj is InvalidInput_ { + return obj.tag === RtcError_Tags.InvalidInput; + } + static hasInner(obj: any): obj is InvalidInput_ { + return InvalidInput_.instanceOf(obj); + } + + static getInner(obj: InvalidInput_): +Readonly< +[string +]> { + return obj.inner; + } + + } + + type Http__interface = { + tag: RtcError_Tags.Http; + inner: +Readonly< +[string +]> + }; + /** + * A homeserver HTTP failure — transient as far as this crate knows. + */ + class Http_ extends UniffiError implements Http__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.Http; + readonly inner: +Readonly< +[string +]>; + constructor(v0: string) { + super("RtcError", "Http"); + + this.inner = Object.freeze([v0]); + } + static new(v0: string): Http_ { + return new Http_(v0); + } + + static instanceOf(obj: any): obj is Http_ { + return obj.tag === RtcError_Tags.Http; + } + static hasInner(obj: any): obj is Http_ { + return Http_.instanceOf(obj); + } + + static getInner(obj: Http_): +Readonly< +[string +]> { + return obj.inner; + } + + } + + type Driver__interface = { + tag: RtcError_Tags.Driver; + inner: +Readonly< +[string +]> + }; + /** + * Anything the driver could not classify. + */ + class Driver_ extends UniffiError implements Driver__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.Driver; + readonly inner: +Readonly< +[string +]>; + constructor(v0: string) { + super("RtcError", "Driver"); + + this.inner = Object.freeze([v0]); + } + static new(v0: string): Driver_ { + return new Driver_(v0); + } + + static instanceOf(obj: any): obj is Driver_ { + return obj.tag === RtcError_Tags.Driver; + } + static hasInner(obj: any): obj is Driver_ { + return Driver_.instanceOf(obj); + } + + static getInner(obj: Driver_): +Readonly< +[string +]> { + return obj.inner; + } + + } + + type Rejected__interface = { + tag: RtcError_Tags.Rejected; + inner: +Readonly< +[string +]> + }; + class Rejected_ extends UniffiError implements Rejected__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.Rejected; + readonly inner: +Readonly< +[string +]>; + constructor(v0: string) { + super("RtcError", "Rejected"); + + this.inner = Object.freeze([v0]); + } + static new(v0: string): Rejected_ { + return new Rejected_(v0); + } + + static instanceOf(obj: any): obj is Rejected_ { + return obj.tag === RtcError_Tags.Rejected; + } + static hasInner(obj: any): obj is Rejected_ { + return Rejected_.instanceOf(obj); + } + + static getInner(obj: Rejected_): +Readonly< +[string +]> { + return obj.inner; + } + + } + + type Unsupported__interface = { + tag: RtcError_Tags.Unsupported; + inner: +Readonly< +[string +]> + }; + class Unsupported_ extends UniffiError implements Unsupported__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.Unsupported; + readonly inner: +Readonly< +[string +]>; + constructor(v0: string) { + super("RtcError", "Unsupported"); + + this.inner = Object.freeze([v0]); + } + static new(v0: string): Unsupported_ { + return new Unsupported_(v0); + } + + static instanceOf(obj: any): obj is Unsupported_ { + return obj.tag === RtcError_Tags.Unsupported; + } + static hasInner(obj: any): obj is Unsupported_ { + return Unsupported_.instanceOf(obj); + } + + static getInner(obj: Unsupported_): +Readonly< +[string +]> { + return obj.inner; + } + + } + + type RateLimited__interface = { + tag: RtcError_Tags.RateLimited; + inner: +Readonly<{retryAfterMs?: bigint}> + }; + /** + * `M_LIMIT_EXCEEDED`: back off for `retry_after_ms` before retrying. + */ + class RateLimited_ extends UniffiError implements RateLimited__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.RateLimited; + readonly inner: +Readonly<{retryAfterMs?: bigint}>; + constructor( +inner: {retryAfterMs?: bigint }) { + super("RtcError", "RateLimited"); + + this.inner = Object.freeze(inner); + } + static new( +inner: {retryAfterMs?: bigint }): RateLimited_ { + return new RateLimited_(inner); + } + + static instanceOf(obj: any): obj is RateLimited_ { + return obj.tag === RtcError_Tags.RateLimited; + } + static hasInner(obj: any): obj is RateLimited_ { + return RateLimited_.instanceOf(obj); + } + + static getInner(obj: RateLimited_): +Readonly<{retryAfterMs?: bigint}> { + return obj.inner; + } + + } + + type Stopped__interface = { + tag: RtcError_Tags.Stopped + }; + /** + * The manager behind the call has stopped; build a new one. + */ + class Stopped_ extends UniffiError implements Stopped__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.Stopped; + constructor() { + super("RtcError", "Stopped"); + } + + static new(): Stopped_ { + return new Stopped_(); + } + + static instanceOf(obj: any): obj is Stopped_ { + return obj.tag === RtcError_Tags.Stopped; + } + static hasInner(obj: any): obj is Stopped_ { + return false; + } + + } + + type AlreadyJoined__interface = { + tag: RtcError_Tags.AlreadyJoined + }; + class AlreadyJoined_ extends UniffiError implements AlreadyJoined__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.AlreadyJoined; + constructor() { + super("RtcError", "AlreadyJoined"); + } + + static new(): AlreadyJoined_ { + return new AlreadyJoined_(); + } + + static instanceOf(obj: any): obj is AlreadyJoined_ { + return obj.tag === RtcError_Tags.AlreadyJoined; + } + static hasInner(obj: any): obj is AlreadyJoined_ { + return false; + } + + } + + type NotJoined__interface = { + tag: RtcError_Tags.NotJoined + }; + class NotJoined_ extends UniffiError implements NotJoined__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.NotJoined; + constructor() { + super("RtcError", "NotJoined"); + } + + static new(): NotJoined_ { + return new NotJoined_(); + } + + static instanceOf(obj: any): obj is NotJoined_ { + return obj.tag === RtcError_Tags.NotJoined; + } + static hasInner(obj: any): obj is NotJoined_ { + return false; + } + + } + + type SlotClosed__interface = { + tag: RtcError_Tags.SlotClosed + }; + class SlotClosed_ extends UniffiError implements SlotClosed__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.SlotClosed; + constructor() { + super("RtcError", "SlotClosed"); + } + + static new(): SlotClosed_ { + return new SlotClosed_(); + } + + static instanceOf(obj: any): obj is SlotClosed_ { + return obj.tag === RtcError_Tags.SlotClosed; + } + static hasInner(obj: any): obj is SlotClosed_ { + return false; + } + + } + + type NoTransport__interface = { + tag: RtcError_Tags.NoTransport; + inner: +Readonly< +[string +]> + }; + /** + * The homeserver advertises no usable RTC transport — a configuration + * problem; retrying will not help. + */ + class NoTransport_ extends UniffiError implements NoTransport__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.NoTransport; + readonly inner: +Readonly< +[string +]>; + constructor(v0: string) { + super("RtcError", "NoTransport"); + + this.inner = Object.freeze([v0]); + } + static new(v0: string): NoTransport_ { + return new NoTransport_(v0); + } + + static instanceOf(obj: any): obj is NoTransport_ { + return obj.tag === RtcError_Tags.NoTransport; + } + static hasInner(obj: any): obj is NoTransport_ { + return NoTransport_.instanceOf(obj); + } + + static getInner(obj: NoTransport_): +Readonly< +[string +]> { + return obj.inner; + } + + } + + type TokenRefused__interface = { + tag: RtcError_Tags.TokenRefused; + inner: +Readonly< +[string +]> + }; + /** + * A transport exists but its token could not be minted — auth or + * network; retrying may help. + */ + class TokenRefused_ extends UniffiError implements TokenRefused__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.TokenRefused; + readonly inner: +Readonly< +[string +]>; + constructor(v0: string) { + super("RtcError", "TokenRefused"); + + this.inner = Object.freeze([v0]); + } + static new(v0: string): TokenRefused_ { + return new TokenRefused_(v0); + } + + static instanceOf(obj: any): obj is TokenRefused_ { + return obj.tag === RtcError_Tags.TokenRefused; + } + static hasInner(obj: any): obj is TokenRefused_ { + return TokenRefused_.instanceOf(obj); + } + + static getInner(obj: TokenRefused_): +Readonly< +[string +]> { + return obj.inner; + } + + } + + type EncryptionSetup__interface = { + tag: RtcError_Tags.EncryptionSetup; + inner: +Readonly< +[string +]> + }; + /** + * A crate precondition, not a caller mistake. + */ + class EncryptionSetup_ extends UniffiError implements EncryptionSetup__interface { + /** + * @private + * This field is private and should not be used, use `tag` instead. + */ + readonly [uniffiTypeNameSymbol] = "RtcError"; + readonly tag = RtcError_Tags.EncryptionSetup; + readonly inner: +Readonly< +[string +]>; + constructor(v0: string) { + super("RtcError", "EncryptionSetup"); + + this.inner = Object.freeze([v0]); + } + static new(v0: string): EncryptionSetup_ { + return new EncryptionSetup_(v0); + } + + static instanceOf(obj: any): obj is EncryptionSetup_ { + return obj.tag === RtcError_Tags.EncryptionSetup; + } + static hasInner(obj: any): obj is EncryptionSetup_ { + return EncryptionSetup_.instanceOf(obj); + } + + static getInner(obj: EncryptionSetup_): +Readonly< +[string +]> { + return obj.inner; + } + + } + + function instanceOf(obj: any): obj is RtcError { + return obj[uniffiTypeNameSymbol] === "RtcError"; + } + + return Object.freeze({ + instanceOf, + InvalidInput: InvalidInput_, + Http: Http_, + Driver: Driver_, + Rejected: Rejected_, + Unsupported: Unsupported_, + RateLimited: RateLimited_, + Stopped: Stopped_, + AlreadyJoined: AlreadyJoined_, + NotJoined: NotJoined_, + SlotClosed: SlotClosed_, + NoTransport: NoTransport_, + TokenRefused: TokenRefused_, + EncryptionSetup: EncryptionSetup_ + }); + +})(); +/** + * Errors across the FFI, both directions. A foreign driver maps its + * homeserver errors onto these: `Rejected` = 403 `M_FORBIDDEN`, + * `Unsupported` = 404 `M_UNRECOGNIZED` — both read as "this homeserver will + * never do delayed events" by the own-membership machine. + */ +export type RtcError = InstanceType< + typeof RtcError['InvalidInput' | 'Http' | 'Driver' | 'Rejected' | 'Unsupported' | 'RateLimited' | 'Stopped' | 'AlreadyJoined' | 'NotJoined' | 'SlotClosed' | 'NoTransport' | 'TokenRefused' | 'EncryptionSetup'] +>; + +// FfiConverter for enum RtcError +const FfiConverterTypeRtcError = (() => { + type TypeName = RtcError; + class FFIConverter extends AbstractFfiConverterByteArray { + readFromCursor(c: Cursor): TypeName { + switch (c.readI32()) { + case 1: return new RtcError.InvalidInput(FfiConverterString.readFromCursor(c)); + case 2: return new RtcError.Http(FfiConverterString.readFromCursor(c)); + case 3: return new RtcError.Driver(FfiConverterString.readFromCursor(c)); + case 4: return new RtcError.Rejected(FfiConverterString.readFromCursor(c)); + case 5: return new RtcError.Unsupported(FfiConverterString.readFromCursor(c)); + case 6: return new RtcError.RateLimited({retryAfterMs: FfiConverterOptionalUInt64.readFromCursor(c) }); + case 7: return new RtcError.Stopped(); + case 8: return new RtcError.AlreadyJoined(); + case 9: return new RtcError.NotJoined(); + case 10: return new RtcError.SlotClosed(); + case 11: return new RtcError.NoTransport(FfiConverterString.readFromCursor(c)); + case 12: return new RtcError.TokenRefused(FfiConverterString.readFromCursor(c)); + case 13: return new RtcError.EncryptionSetup(FfiConverterString.readFromCursor(c)); + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + writeIntoCursor(value: TypeName, c: Cursor): void { + switch (value.tag) { + case RtcError_Tags.InvalidInput: { + c.writeI32(1); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner[0], c); + return; + } + case RtcError_Tags.Http: { + c.writeI32(2); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner[0], c); + return; + } + case RtcError_Tags.Driver: { + c.writeI32(3); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner[0], c); + return; + } + case RtcError_Tags.Rejected: { + c.writeI32(4); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner[0], c); + return; + } + case RtcError_Tags.Unsupported: { + c.writeI32(5); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner[0], c); + return; + } + case RtcError_Tags.RateLimited: { + c.writeI32(6); + const inner = value.inner; + FfiConverterOptionalUInt64.writeIntoCursor(inner.retryAfterMs, c); + return; + } + case RtcError_Tags.Stopped: { + c.writeI32(7); + return; + } + case RtcError_Tags.AlreadyJoined: { + c.writeI32(8); + return; + } + case RtcError_Tags.NotJoined: { + c.writeI32(9); + return; + } + case RtcError_Tags.SlotClosed: { + c.writeI32(10); + return; + } + case RtcError_Tags.NoTransport: { + c.writeI32(11); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner[0], c); + return; + } + case RtcError_Tags.TokenRefused: { + c.writeI32(12); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner[0], c); + return; + } + case RtcError_Tags.EncryptionSetup: { + c.writeI32(13); + const inner = value.inner; + FfiConverterString.writeIntoCursor(inner[0], c); + return; + } + default: + // Throwing from here means that RtcError_Tags hasn't matched an ordinal. + throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + allocationSize(value: TypeName): number { + switch (value.tag) { + case RtcError_Tags.InvalidInput: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner[0]); + return size; + } + case RtcError_Tags.Http: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner[0]); + return size; + } + case RtcError_Tags.Driver: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner[0]); + return size; + } + case RtcError_Tags.Rejected: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner[0]); + return size; + } + case RtcError_Tags.Unsupported: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner[0]); + return size; + } + case RtcError_Tags.RateLimited: { + const inner = value.inner; + let size = 4; + size += FfiConverterOptionalUInt64.allocationSize(inner.retryAfterMs); + return size; + } + case RtcError_Tags.Stopped: { + return 4; + } + case RtcError_Tags.AlreadyJoined: { + return 4; + } + case RtcError_Tags.NotJoined: { + return 4; + } + case RtcError_Tags.SlotClosed: { + return 4; + } + case RtcError_Tags.NoTransport: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner[0]); + return size; + } + case RtcError_Tags.TokenRefused: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner[0]); + return size; + } + case RtcError_Tags.EncryptionSetup: { + const inner = value.inner; + let size = 4; + size += FfiConverterString.allocationSize(inner[0]); + return size; + } + default: throw new UniffiInternalError.UnexpectedEnumCase(); + } + } + } + return new FFIConverter(); +})(); + +export interface ConnectionsListener { + + onConnectionsChange(connections: Array): void; +} + + +export class ConnectionsListenerImpl extends UniffiAbstractObject implements ConnectionsListener { + + readonly [uniffiTypeNameSymbol] = "ConnectionsListenerImpl"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeConnectionsListenerImplObjectFactory.bless(pointer); +} + + + + + onConnectionsChange(connections: Array): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_connectionslistener_on_connections_change( + uniffiTypeConnectionsListenerImplObjectFactory.clonePointer(this), + FfiConverterSequenceTypeFfiConnectionWithMembers.lower(connections, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeConnectionsListenerImplObjectFactory.pointer(this); + uniffiTypeConnectionsListenerImplObjectFactory.freePointer(pointer); + uniffiTypeConnectionsListenerImplObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is ConnectionsListenerImpl { + return uniffiTypeConnectionsListenerImplObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeConnectionsListenerImplObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeConnectionsListenerImplObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): ConnectionsListener { + const instance = Object.create(ConnectionsListenerImpl.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "ConnectionsListenerImpl"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: ConnectionsListener): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: ConnectionsListener): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_connectionslistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_connectionslistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is ConnectionsListener { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "ConnectionsListenerImpl"; + }, +}})(); +const FfiConverterTypeConnectionsListener = new FfiConverterObjectWithCallbacks(uniffiTypeConnectionsListenerImplObjectFactory); + +// Add a vtable for the callbacks that go in ConnectionsListener. + +// Put the implementation in a struct so we don't pollute the top-level namespace +const uniffiCallbackInterfaceConnectionsListener: { vtable: any; register: () => void; } = { + // Create the VTable using a series of closures. + // ts automatically converts these into C callback functions. + vtable: { + on_connections_change: ( + uniffiHandle: bigint, + connections: Uint8Array,) => { + const uniffiMakeCall = + () + : void => { + const jsCallback = FfiConverterTypeConnectionsListener.lift(uniffiHandle); + return jsCallback.onConnectionsChange( + FfiConverterSequenceTypeFfiConnectionWithMembers.lift(connections) + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => {}; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + uniffi_free: (uniffiHandle: UniffiHandle): void => { + // this will throw a stale handle error if the handle isn't found. + FfiConverterTypeConnectionsListener.drop(uniffiHandle); + }, + uniffi_clone: (uniffiHandle: UniffiHandle): UniffiHandle => { + return FfiConverterTypeConnectionsListener.clone(uniffiHandle); + } + }, + register: () => {nativeModule().ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_connectionslistener( + uniffiCallbackInterfaceConnectionsListener.vtable + ); + }, +}; + +/** + * One end of the driver's connectivity stream: the host emits the new + * verdict whenever its homeserver connection comes or goes (a syncing + * client: sync running or not). `false` from `emit` means no consumer is + * left — unhook the handler. + */ +export interface ConnectivitySinkLike { + + emit(connected: boolean): boolean; +} +/** + * @deprecated Use `ConnectivitySinkLike` instead. + */ +export type ConnectivitySinkInterface = ConnectivitySinkLike; + + +/** + * One end of the driver's connectivity stream: the host emits the new + * verdict whenever its homeserver connection comes or goes (a syncing + * client: sync running or not). `false` from `emit` means no consumer is + * left — unhook the handler. + */ +export class ConnectivitySink extends UniffiAbstractObject implements ConnectivitySinkLike { + + readonly [uniffiTypeNameSymbol] = "ConnectivitySink"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeConnectivitySinkObjectFactory.bless(pointer); +} + + + + + emit(connected: boolean): boolean { + return FfiConverterBool.lift(uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_connectivitysink_emit( + uniffiTypeConnectivitySinkObjectFactory.clonePointer(this), + FfiConverterBool.lower(connected, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + )); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeConnectivitySinkObjectFactory.pointer(this); + uniffiTypeConnectivitySinkObjectFactory.freePointer(pointer); + uniffiTypeConnectivitySinkObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is ConnectivitySink { + return uniffiTypeConnectivitySinkObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeConnectivitySinkObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeConnectivitySinkObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): ConnectivitySinkLike { + const instance = Object.create(ConnectivitySink.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "ConnectivitySink"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: ConnectivitySinkLike): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: ConnectivitySinkLike): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_connectivitysink(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_connectivitysink(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is ConnectivitySinkLike { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "ConnectivitySink"; + }, +}})(); +const FfiConverterTypeConnectivitySink = new FfiConverterObject(uniffiTypeConnectivitySinkObjectFactory); + +/** + * The FFI driver object — the one place a foreign [`MatrixDriverCallback`] + * becomes a [`crate::driver::MatrixDriver`]. Room-scoped and room-lived, + * like matrix-rust-sdk's widget `MatrixDriver`: construct it once per room + * and share it across managers (one room can hold several slots). + * + * It is both the FFI handle *and* the adapter: the trait impls below + * translate JSON-string payloads and FFI records into the crate's driver + * types. The foreign `subscribe_*` handshake happens in [`Self::new`], each + * sink feeding one inbound channel; the Rust trait's `subscribe_*` methods + * (fresh receiver per call) are served by fanning those channels out + * internally — so any number of managers consume a foreign driver exactly + * like a native one. + */ +export interface FfiMatrixDriverLike { + +} +/** + * @deprecated Use `FfiMatrixDriverLike` instead. + */ +export type FfiMatrixDriverInterface = FfiMatrixDriverLike; + + +/** + * The FFI driver object — the one place a foreign [`MatrixDriverCallback`] + * becomes a [`crate::driver::MatrixDriver`]. Room-scoped and room-lived, + * like matrix-rust-sdk's widget `MatrixDriver`: construct it once per room + * and share it across managers (one room can hold several slots). + * + * It is both the FFI handle *and* the adapter: the trait impls below + * translate JSON-string payloads and FFI records into the crate's driver + * types. The foreign `subscribe_*` handshake happens in [`Self::new`], each + * sink feeding one inbound channel; the Rust trait's `subscribe_*` methods + * (fresh receiver per call) are served by fanning those channels out + * internally — so any number of managers consume a foreign driver exactly + * like a native one. + */ +export class FfiMatrixDriver extends UniffiAbstractObject implements FfiMatrixDriverLike { + + readonly [uniffiTypeNameSymbol] = "FfiMatrixDriver"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; +/** + * Performs the `subscribe_*` handshake with the foreign driver + * (synchronously, exactly once). + */ + constructor(callback: MatrixDriverCallback) { + super(); + const pointer = + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_constructor_ffimatrixdriver_new( + FfiConverterTypeMatrixDriverCallback.lower(callback, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeFfiMatrixDriverObjectFactory.bless(pointer); + } + + + + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeFfiMatrixDriverObjectFactory.pointer(this); + uniffiTypeFfiMatrixDriverObjectFactory.freePointer(pointer); + uniffiTypeFfiMatrixDriverObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is FfiMatrixDriver { + return uniffiTypeFfiMatrixDriverObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeFfiMatrixDriverObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeFfiMatrixDriverObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): FfiMatrixDriverLike { + const instance = Object.create(FfiMatrixDriver.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "FfiMatrixDriver"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: FfiMatrixDriverLike): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: FfiMatrixDriverLike): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_ffimatrixdriver(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_ffimatrixdriver(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is FfiMatrixDriverLike { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "FfiMatrixDriver"; + }, +}})(); +const FfiConverterTypeFfiMatrixDriver = new FfiConverterObject(uniffiTypeFfiMatrixDriverObjectFactory); + +export interface KeyMapListener { + +/** + * The full map plus the one key that changed — route `change` to the LK + * room(s) of that member. + */ + onKeyMapChange(keyMap: Array, change: FfiMediaKey): void; +} + + +export class KeyMapListenerImpl extends UniffiAbstractObject implements KeyMapListener { + + readonly [uniffiTypeNameSymbol] = "KeyMapListenerImpl"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeKeyMapListenerImplObjectFactory.bless(pointer); +} + + + + +/** + * The full map plus the one key that changed — route `change` to the LK + * room(s) of that member. + */ + onKeyMapChange(keyMap: Array, change: FfiMediaKey): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_keymaplistener_on_key_map_change( + uniffiTypeKeyMapListenerImplObjectFactory.clonePointer(this), + FfiConverterSequenceTypeFfiMediaKey.lower(keyMap, nativeModule().rustbuffer_alloc), + FfiConverterTypeFfiMediaKey.lower(change, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeKeyMapListenerImplObjectFactory.pointer(this); + uniffiTypeKeyMapListenerImplObjectFactory.freePointer(pointer); + uniffiTypeKeyMapListenerImplObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is KeyMapListenerImpl { + return uniffiTypeKeyMapListenerImplObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeKeyMapListenerImplObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeKeyMapListenerImplObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): KeyMapListener { + const instance = Object.create(KeyMapListenerImpl.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "KeyMapListenerImpl"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: KeyMapListener): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: KeyMapListener): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_keymaplistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_keymaplistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is KeyMapListener { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "KeyMapListenerImpl"; + }, +}})(); +const FfiConverterTypeKeyMapListener = new FfiConverterObjectWithCallbacks(uniffiTypeKeyMapListenerImplObjectFactory); + +// Add a vtable for the callbacks that go in KeyMapListener. + +// Put the implementation in a struct so we don't pollute the top-level namespace +const uniffiCallbackInterfaceKeyMapListener: { vtable: any; register: () => void; } = { + // Create the VTable using a series of closures. + // ts automatically converts these into C callback functions. + vtable: { + on_key_map_change: ( + uniffiHandle: bigint, + keyMap: Uint8Array, + change: Uint8Array,) => { + const uniffiMakeCall = + () + : void => { + const jsCallback = FfiConverterTypeKeyMapListener.lift(uniffiHandle); + return jsCallback.onKeyMapChange( + FfiConverterSequenceTypeFfiMediaKey.lift(keyMap), + FfiConverterTypeFfiMediaKey.lift(change) + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => {}; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + uniffi_free: (uniffiHandle: UniffiHandle): void => { + // this will throw a stale handle error if the handle isn't found. + FfiConverterTypeKeyMapListener.drop(uniffiHandle); + }, + uniffi_clone: (uniffiHandle: UniffiHandle): UniffiHandle => { + return FfiConverterTypeKeyMapListener.clone(uniffiHandle); + } + }, + register: () => {nativeModule().ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_keymaplistener( + uniffiCallbackInterfaceKeyMapListener.vtable + ); + }, +}; + +export interface KeyRejectedListener { + +/** + * An inbound media key was discarded: `member_id` names whose, `reason` + * says why. + * + * **Secondary** to `FfiMembership::media_key.rejection` and + * `FfiImpairment::MediaKeyRejected`, which a UI attaching late still + * finds; this is for logging and telemetry. + */ + onKeyRejected(memberId: string, reason: FfiKeyRejection): void; +} + + +export class KeyRejectedListenerImpl extends UniffiAbstractObject implements KeyRejectedListener { + + readonly [uniffiTypeNameSymbol] = "KeyRejectedListenerImpl"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeKeyRejectedListenerImplObjectFactory.bless(pointer); +} + + + + +/** + * An inbound media key was discarded: `member_id` names whose, `reason` + * says why. + * + * **Secondary** to `FfiMembership::media_key.rejection` and + * `FfiImpairment::MediaKeyRejected`, which a UI attaching late still + * finds; this is for logging and telemetry. + */ + onKeyRejected(memberId: string, reason: FfiKeyRejection): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_keyrejectedlistener_on_key_rejected( + uniffiTypeKeyRejectedListenerImplObjectFactory.clonePointer(this), + FfiConverterString.lower(memberId, nativeModule().rustbuffer_alloc), + FfiConverterTypeFfiKeyRejection.lower(reason, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeKeyRejectedListenerImplObjectFactory.pointer(this); + uniffiTypeKeyRejectedListenerImplObjectFactory.freePointer(pointer); + uniffiTypeKeyRejectedListenerImplObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is KeyRejectedListenerImpl { + return uniffiTypeKeyRejectedListenerImplObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeKeyRejectedListenerImplObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeKeyRejectedListenerImplObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): KeyRejectedListener { + const instance = Object.create(KeyRejectedListenerImpl.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "KeyRejectedListenerImpl"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: KeyRejectedListener): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: KeyRejectedListener): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_keyrejectedlistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_keyrejectedlistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is KeyRejectedListener { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "KeyRejectedListenerImpl"; + }, +}})(); +const FfiConverterTypeKeyRejectedListener = new FfiConverterObjectWithCallbacks(uniffiTypeKeyRejectedListenerImplObjectFactory); + +// Add a vtable for the callbacks that go in KeyRejectedListener. + +// Put the implementation in a struct so we don't pollute the top-level namespace +const uniffiCallbackInterfaceKeyRejectedListener: { vtable: any; register: () => void; } = { + // Create the VTable using a series of closures. + // ts automatically converts these into C callback functions. + vtable: { + on_key_rejected: ( + uniffiHandle: bigint, + memberId: Uint8Array, + reason: Uint8Array,) => { + const uniffiMakeCall = + () + : void => { + const jsCallback = FfiConverterTypeKeyRejectedListener.lift(uniffiHandle); + return jsCallback.onKeyRejected( + FfiConverterString.lift(memberId), + FfiConverterTypeFfiKeyRejection.lift(reason) + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => {}; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + uniffi_free: (uniffiHandle: UniffiHandle): void => { + // this will throw a stale handle error if the handle isn't found. + FfiConverterTypeKeyRejectedListener.drop(uniffiHandle); + }, + uniffi_clone: (uniffiHandle: UniffiHandle): UniffiHandle => { + return FfiConverterTypeKeyRejectedListener.clone(uniffiHandle); + } + }, + register: () => {nativeModule().ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_keyrejectedlistener( + uniffiCallbackInterfaceKeyRejectedListener.vtable + ); + }, +}; + +export interface MembershipsListener { + +/** + * One tile per membership; use the connections output to acquire each + * tile's media (`membership.connections` -> LK room, + * `membership.transport_identity` -> participant). + */ + onMembershipsChange(memberships: Array): void; +} + + +export class MembershipsListenerImpl extends UniffiAbstractObject implements MembershipsListener { + + readonly [uniffiTypeNameSymbol] = "MembershipsListenerImpl"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeMembershipsListenerImplObjectFactory.bless(pointer); +} + + + + +/** + * One tile per membership; use the connections output to acquire each + * tile's media (`membership.connections` -> LK room, + * `membership.transport_identity` -> participant). + */ + onMembershipsChange(memberships: Array): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_membershipslistener_on_memberships_change( + uniffiTypeMembershipsListenerImplObjectFactory.clonePointer(this), + FfiConverterSequenceTypeFfiMembership.lower(memberships, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeMembershipsListenerImplObjectFactory.pointer(this); + uniffiTypeMembershipsListenerImplObjectFactory.freePointer(pointer); + uniffiTypeMembershipsListenerImplObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is MembershipsListenerImpl { + return uniffiTypeMembershipsListenerImplObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeMembershipsListenerImplObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeMembershipsListenerImplObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): MembershipsListener { + const instance = Object.create(MembershipsListenerImpl.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "MembershipsListenerImpl"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: MembershipsListener): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: MembershipsListener): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_membershipslistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_membershipslistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is MembershipsListener { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "MembershipsListenerImpl"; + }, +}})(); +const FfiConverterTypeMembershipsListener = new FfiConverterObjectWithCallbacks(uniffiTypeMembershipsListenerImplObjectFactory); + +// Add a vtable for the callbacks that go in MembershipsListener. + +// Put the implementation in a struct so we don't pollute the top-level namespace +const uniffiCallbackInterfaceMembershipsListener: { vtable: any; register: () => void; } = { + // Create the VTable using a series of closures. + // ts automatically converts these into C callback functions. + vtable: { + on_memberships_change: ( + uniffiHandle: bigint, + memberships: Uint8Array,) => { + const uniffiMakeCall = + () + : void => { + const jsCallback = FfiConverterTypeMembershipsListener.lift(uniffiHandle); + return jsCallback.onMembershipsChange( + FfiConverterSequenceTypeFfiMembership.lift(memberships) + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => {}; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + uniffi_free: (uniffiHandle: UniffiHandle): void => { + // this will throw a stale handle error if the handle isn't found. + FfiConverterTypeMembershipsListener.drop(uniffiHandle); + }, + uniffi_clone: (uniffiHandle: UniffiHandle): UniffiHandle => { + return FfiConverterTypeMembershipsListener.clone(uniffiHandle); + } + }, + register: () => {nativeModule().ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_membershipslistener( + uniffiCallbackInterfaceMembershipsListener.vtable + ); + }, +}; + +export interface StatusListener { + + onStatusChange(status: FfiStatus): void; +} + + +export class StatusListenerImpl extends UniffiAbstractObject implements StatusListener { + + readonly [uniffiTypeNameSymbol] = "StatusListenerImpl"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeStatusListenerImplObjectFactory.bless(pointer); +} + + + + + onStatusChange(status: FfiStatus): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_statuslistener_on_status_change( + uniffiTypeStatusListenerImplObjectFactory.clonePointer(this), + FfiConverterTypeFfiStatus.lower(status, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeStatusListenerImplObjectFactory.pointer(this); + uniffiTypeStatusListenerImplObjectFactory.freePointer(pointer); + uniffiTypeStatusListenerImplObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is StatusListenerImpl { + return uniffiTypeStatusListenerImplObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeStatusListenerImplObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeStatusListenerImplObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): StatusListener { + const instance = Object.create(StatusListenerImpl.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "StatusListenerImpl"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: StatusListener): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: StatusListener): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_statuslistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_statuslistener(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is StatusListener { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "StatusListenerImpl"; + }, +}})(); +const FfiConverterTypeStatusListener = new FfiConverterObjectWithCallbacks(uniffiTypeStatusListenerImplObjectFactory); + +// Add a vtable for the callbacks that go in StatusListener. + +// Put the implementation in a struct so we don't pollute the top-level namespace +const uniffiCallbackInterfaceStatusListener: { vtable: any; register: () => void; } = { + // Create the VTable using a series of closures. + // ts automatically converts these into C callback functions. + vtable: { + on_status_change: ( + uniffiHandle: bigint, + status: Uint8Array,) => { + const uniffiMakeCall = + () + : void => { + const jsCallback = FfiConverterTypeStatusListener.lift(uniffiHandle); + return jsCallback.onStatusChange( + FfiConverterTypeFfiStatus.lift(status) + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => {}; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + uniffi_free: (uniffiHandle: UniffiHandle): void => { + // this will throw a stale handle error if the handle isn't found. + FfiConverterTypeStatusListener.drop(uniffiHandle); + }, + uniffi_clone: (uniffiHandle: UniffiHandle): UniffiHandle => { + return FfiConverterTypeStatusListener.clone(uniffiHandle); + } + }, + register: () => {nativeModule().ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_statuslistener( + uniffiCallbackInterfaceStatusListener.vtable + ); + }, +}; + +/** + * FFI wrapper around [`ParticipationManager`]. + */ +export interface FfiParticipationManagerLike { + + closeSlot(asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; +/** + * Wanted connections the host cannot currently use, with the members + * whose media is affected. Empty is the healthy case. + */ + connectionProblems(): Array; + connections(): Array; +/** + * Diagnostics JSON: an unversioned dump for bug reports, **not** a UI + * contract. Everything a UI needs is typed on `status()`, + * `session()`, `memberships()` and `connection_problems()`. + */ + debugSnapshot(): string; +/** + * Whether the driver currently reports the homeserver reachable. Inside a + * participation the same fact is `FfiImpairment::HomeserverUnreachable` + * in the status; this covers the lobby and the post-call screen. + */ + isHomeserverConnected(): boolean; + join(intent: FfiTransportIntent, params: FfiJoinParams, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; + keyMap(): Array; +/** + * `code` defaults to MSC4143's plain `leave`. + */ + leave(code: string | undefined, reason: string | undefined, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; +/** + * The session's joined projection plus left members still holding our + * keys (see [`FfiMembershipState`]). + */ + memberships(): Array; + openSlot(applicationType: string, encrypted: boolean, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; +/** + * Open this manager's slot (`m.per_member` encryption when `encrypted`). + * Our member id for this participation; `None` while not joined. + * + * Every self-referential check needs it — am I in the roster + * (`session().excluded_candidates`), which LiveKit participant is me + * (`own_membership().transport_identity`). Matching on + * `(user_id, device_id)` is not a substitute: one device may hold + * several RTC members, and a rejoin mints a fresh id. + */ + ownMemberId(): string | undefined; +/** + * Our own entry in `memberships()`, when the session projects it. + */ + ownMembership(): FfiMembership | undefined; +/** + * Our LiveKit participant identity, known from the moment `join()` + * starts (before our membership echo). `None` while not joined. + */ + ownTransportIdentity(): string | undefined; +/** + * The live session snapshot (slot open?, encrypted?, members, start + * time) — the same record `compute_sessions_from_events` returns. + */ + session(): FfiSessionSnapshot; + setConnectionsListener(listener: ConnectionsListener): void; + setKeyMapListener(listener: KeyMapListener): void; + setKeyRejectedListener(listener: KeyRejectedListener): void; + setMembershipsListener(listener: MembershipsListener): void; + setStatusListener(listener: StatusListener): void; + status(): FfiStatus; +/** + * Change `application["m.call.intent"]` (e.g. `"audio"` / `"video"`) + * while joined: the membership is re-published with it. `None` removes + * the intent. Rejects with `NotJoined` outside a participation. + */ + updateApplication(intent: string | undefined, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; +} +/** + * @deprecated Use `FfiParticipationManagerLike` instead. + */ +export type FfiParticipationManagerInterface = FfiParticipationManagerLike; + + +/** + * FFI wrapper around [`ParticipationManager`]. + */ +export class FfiParticipationManager extends UniffiAbstractObject implements FfiParticipationManagerLike { + + readonly [uniffiTypeNameSymbol] = "FfiParticipationManager"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; +/** + * One manager per `(room, slot)`; `user_id`/`device_id` are who we + * publish as. Any number of managers may share one driver. + */ + constructor(roomId: string, slotId: string, userId: string, deviceId: string, driver: FfiMatrixDriverLike, config: FfiParticipationConfig) { + super(); + const pointer = + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_constructor_ffiparticipationmanager_new( + FfiConverterString.lower(roomId, nativeModule().rustbuffer_alloc), + FfiConverterString.lower(slotId, nativeModule().rustbuffer_alloc), + FfiConverterString.lower(userId, nativeModule().rustbuffer_alloc), + FfiConverterString.lower(deviceId, nativeModule().rustbuffer_alloc), + FfiConverterTypeFfiMatrixDriver.lower(driver, nativeModule().rustbuffer_alloc), + FfiConverterTypeFfiParticipationConfig.lower(config, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeFfiParticipationManagerObjectFactory.bless(pointer); + } + + + + + async closeSlot(asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_close_slot( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_void, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_void, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_void, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_void, + /*liftFunc:*/ (_v) => {}, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * Wanted connections the host cannot currently use, with the members + * whose media is affected. Empty is the healthy case. + */ + connectionProblems(): Array { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_connection_problems( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterSequenceTypeFfiConnectionProblem.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + + connections(): Array { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_connections( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterSequenceTypeFfiConnectionWithMembers.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + +/** + * Diagnostics JSON: an unversioned dump for bug reports, **not** a UI + * contract. Everything a UI needs is typed on `status()`, + * `session()`, `memberships()` and `connection_problems()`. + */ + debugSnapshot(): string { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_debug_snapshot( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterString.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + +/** + * Whether the driver currently reports the homeserver reachable. Inside a + * participation the same fact is `FfiImpairment::HomeserverUnreachable` + * in the status; this covers the lobby and the post-call screen. + */ + isHomeserverConnected(): boolean { + return FfiConverterBool.lift(uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_is_homeserver_connected( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + )); + } + + async join(intent: FfiTransportIntent, params: FfiJoinParams, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_join( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this),FfiConverterTypeFfiTransportIntent.lower(intent, nativeModule().rustbuffer_alloc),FfiConverterTypeFfiJoinParams.lower(params, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_void, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_void, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_void, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_void, + /*liftFunc:*/ (_v) => {}, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + + keyMap(): Array { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_key_map( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterSequenceTypeFfiMediaKey.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + +/** + * `code` defaults to MSC4143's plain `leave`. + */ + async leave(code: string | undefined, reason: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_leave( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this),FfiConverterOptionalString.lower(code, nativeModule().rustbuffer_alloc),FfiConverterOptionalString.lower(reason, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_void, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_void, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_void, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_void, + /*liftFunc:*/ (_v) => {}, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * The session's joined projection plus left members still holding our + * keys (see [`FfiMembershipState`]). + */ + memberships(): Array { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_memberships( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterSequenceTypeFfiMembership.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + + async openSlot(applicationType: string, encrypted: boolean, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_open_slot( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this),FfiConverterString.lower(applicationType, nativeModule().rustbuffer_alloc),FfiConverterBool.lower(encrypted, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_void, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_void, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_void, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_void, + /*liftFunc:*/ (_v) => {}, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * Open this manager's slot (`m.per_member` encryption when `encrypted`). + * Our member id for this participation; `None` while not joined. + * + * Every self-referential check needs it — am I in the roster + * (`session().excluded_candidates`), which LiveKit participant is me + * (`own_membership().transport_identity`). Matching on + * `(user_id, device_id)` is not a substitute: one device may hold + * several RTC members, and a rejoin mints a fresh id. + */ + ownMemberId(): string | undefined { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_own_member_id( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterOptionalString.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + +/** + * Our own entry in `memberships()`, when the session projects it. + */ + ownMembership(): FfiMembership | undefined { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_own_membership( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterOptionalTypeFfiMembership.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + +/** + * Our LiveKit participant identity, known from the moment `join()` + * starts (before our membership echo). `None` while not joined. + */ + ownTransportIdentity(): string | undefined { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_own_transport_identity( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterOptionalString.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + +/** + * The live session snapshot (slot open?, encrypted?, members, start + * time) — the same record `compute_sessions_from_events` returns. + */ + session(): FfiSessionSnapshot { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_session( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterTypeFfiSessionSnapshot.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + + setConnectionsListener(listener: ConnectionsListener): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_connections_listener( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + FfiConverterTypeConnectionsListener.lower(listener, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + setKeyMapListener(listener: KeyMapListener): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_key_map_listener( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + FfiConverterTypeKeyMapListener.lower(listener, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + setKeyRejectedListener(listener: KeyRejectedListener): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_key_rejected_listener( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + FfiConverterTypeKeyRejectedListener.lower(listener, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + setMembershipsListener(listener: MembershipsListener): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_memberships_listener( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + FfiConverterTypeMembershipsListener.lower(listener, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + setStatusListener(listener: StatusListener): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_status_listener( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + FfiConverterTypeStatusListener.lower(listener, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + status(): FfiStatus { + const __rb: Uint8Array = uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_status( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + try { + return FfiConverterTypeFfiStatus.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + } + +/** + * Change `application["m.call.intent"]` (e.g. `"audio"` / `"video"`) + * while joined: the membership is re-published with it. `None` removes + * the intent. Rejects with `NotJoined` outside a participation. + */ + async updateApplication(intent: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_update_application( + uniffiTypeFfiParticipationManagerObjectFactory.clonePointer(this),FfiConverterOptionalString.lower(intent, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_void, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_void, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_void, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_void, + /*liftFunc:*/ (_v) => {}, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeFfiParticipationManagerObjectFactory.pointer(this); + uniffiTypeFfiParticipationManagerObjectFactory.freePointer(pointer); + uniffiTypeFfiParticipationManagerObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is FfiParticipationManager { + return uniffiTypeFfiParticipationManagerObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeFfiParticipationManagerObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeFfiParticipationManagerObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): FfiParticipationManagerLike { + const instance = Object.create(FfiParticipationManager.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "FfiParticipationManager"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: FfiParticipationManagerLike): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: FfiParticipationManagerLike): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_ffiparticipationmanager(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_ffiparticipationmanager(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is FfiParticipationManagerLike { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "FfiParticipationManager"; + }, +}})(); +const FfiConverterTypeFfiParticipationManager = new FfiConverterObject(uniffiTypeFfiParticipationManagerObjectFactory); + +/** + * One end of a driver event stream: Rust-exported objects handed to the + * foreign driver through the `subscribe_*` methods. The host calls `emit` + * from its own event handlers (e.g. matrix-js-sdk listeners); `false` means + * the Rust side dropped the stream — unhook the handler (this plays the + * role of matrix-rust-sdk's `EventHandlerDropGuard`). + */ +export interface RoomEventSinkLike { + +/** + * Any room event — sticky or state; the session dispatches on type. + * `event_json` is the full event object (see `session::dispatch`), the + * *decrypted* one for encrypted events, with `origin` carrying the + * decryption metadata. Returns `false` once no consumer is left. + */ + emit(eventJson: string, origin: FfiEventOrigin): boolean; +} +/** + * @deprecated Use `RoomEventSinkLike` instead. + */ +export type RoomEventSinkInterface = RoomEventSinkLike; + + +/** + * One end of a driver event stream: Rust-exported objects handed to the + * foreign driver through the `subscribe_*` methods. The host calls `emit` + * from its own event handlers (e.g. matrix-js-sdk listeners); `false` means + * the Rust side dropped the stream — unhook the handler (this plays the + * role of matrix-rust-sdk's `EventHandlerDropGuard`). + */ +export class RoomEventSink extends UniffiAbstractObject implements RoomEventSinkLike { + + readonly [uniffiTypeNameSymbol] = "RoomEventSink"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeRoomEventSinkObjectFactory.bless(pointer); +} + + + + +/** + * Any room event — sticky or state; the session dispatches on type. + * `event_json` is the full event object (see `session::dispatch`), the + * *decrypted* one for encrypted events, with `origin` carrying the + * decryption metadata. Returns `false` once no consumer is left. + */ + emit(eventJson: string, origin: FfiEventOrigin): boolean { + return FfiConverterBool.lift(uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_roomeventsink_emit( + uniffiTypeRoomEventSinkObjectFactory.clonePointer(this), + FfiConverterString.lower(eventJson, nativeModule().rustbuffer_alloc), + FfiConverterTypeFfiEventOrigin.lower(origin, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + )); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeRoomEventSinkObjectFactory.pointer(this); + uniffiTypeRoomEventSinkObjectFactory.freePointer(pointer); + uniffiTypeRoomEventSinkObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is RoomEventSink { + return uniffiTypeRoomEventSinkObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeRoomEventSinkObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeRoomEventSinkObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): RoomEventSinkLike { + const instance = Object.create(RoomEventSink.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "RoomEventSink"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: RoomEventSinkLike): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: RoomEventSinkLike): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_roomeventsink(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_roomeventsink(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is RoomEventSinkLike { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "RoomEventSink"; + }, +}})(); +const FfiConverterTypeRoomEventSink = new FfiConverterObject(uniffiTypeRoomEventSinkObjectFactory); + +export interface ToDeviceSinkLike { + +/** + * A decrypted to-device message with its origin metadata. + * `sender_cross_signed` is the MSC4153 verdict on the sending device + * (`None` = the host cannot tell, treated as not signed: media keys + * from such devices are rejected unless configured otherwise). + */ + emit(eventType: string, sender: string, contentJson: string, origin: FfiEventOrigin, senderCrossSigned: boolean | undefined): boolean; +} +/** + * @deprecated Use `ToDeviceSinkLike` instead. + */ +export type ToDeviceSinkInterface = ToDeviceSinkLike; + + +export class ToDeviceSink extends UniffiAbstractObject implements ToDeviceSinkLike { + + readonly [uniffiTypeNameSymbol] = "ToDeviceSink"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeToDeviceSinkObjectFactory.bless(pointer); +} + + + + +/** + * A decrypted to-device message with its origin metadata. + * `sender_cross_signed` is the MSC4153 verdict on the sending device + * (`None` = the host cannot tell, treated as not signed: media keys + * from such devices are rejected unless configured otherwise). + */ + emit(eventType: string, sender: string, contentJson: string, origin: FfiEventOrigin, senderCrossSigned: boolean | undefined): boolean { + return FfiConverterBool.lift(uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_todevicesink_emit( + uniffiTypeToDeviceSinkObjectFactory.clonePointer(this), + FfiConverterString.lower(eventType, nativeModule().rustbuffer_alloc), + FfiConverterString.lower(sender, nativeModule().rustbuffer_alloc), + FfiConverterString.lower(contentJson, nativeModule().rustbuffer_alloc), + FfiConverterTypeFfiEventOrigin.lower(origin, nativeModule().rustbuffer_alloc), + FfiConverterOptionalBoolean.lower(senderCrossSigned, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + )); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeToDeviceSinkObjectFactory.pointer(this); + uniffiTypeToDeviceSinkObjectFactory.freePointer(pointer); + uniffiTypeToDeviceSinkObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is ToDeviceSink { + return uniffiTypeToDeviceSinkObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeToDeviceSinkObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeToDeviceSinkObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): ToDeviceSinkLike { + const instance = Object.create(ToDeviceSink.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "ToDeviceSink"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: ToDeviceSinkLike): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: ToDeviceSinkLike): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_todevicesink(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_todevicesink(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is ToDeviceSinkLike { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "ToDeviceSink"; + }, +}})(); +const FfiConverterTypeToDeviceSink = new FfiConverterObject(uniffiTypeToDeviceSinkObjectFactory); + +export interface StateUpdateSinkLike { + +/** + * A batch of changed room-state events (applied atomically by the + * session: one snapshot per batch). State events are never encrypted, + * so their origin is `Cleartext`. + */ + emit(eventsJson: Array): boolean; +} +/** + * @deprecated Use `StateUpdateSinkLike` instead. + */ +export type StateUpdateSinkInterface = StateUpdateSinkLike; + + +export class StateUpdateSink extends UniffiAbstractObject implements StateUpdateSinkLike { + + readonly [uniffiTypeNameSymbol] = "StateUpdateSink"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeStateUpdateSinkObjectFactory.bless(pointer); +} + + + + +/** + * A batch of changed room-state events (applied atomically by the + * session: one snapshot per batch). State events are never encrypted, + * so their origin is `Cleartext`. + */ + emit(eventsJson: Array): boolean { + return FfiConverterBool.lift(uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_stateupdatesink_emit( + uniffiTypeStateUpdateSinkObjectFactory.clonePointer(this), + FfiConverterSequenceString.lower(eventsJson, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + )); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeStateUpdateSinkObjectFactory.pointer(this); + uniffiTypeStateUpdateSinkObjectFactory.freePointer(pointer); + uniffiTypeStateUpdateSinkObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is StateUpdateSink { + return uniffiTypeStateUpdateSinkObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeStateUpdateSinkObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeStateUpdateSinkObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): StateUpdateSinkLike { + const instance = Object.create(StateUpdateSink.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "StateUpdateSink"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: StateUpdateSinkLike): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: StateUpdateSinkLike): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_stateupdatesink(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_stateupdatesink(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is StateUpdateSinkLike { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "StateUpdateSink"; + }, +}})(); +const FfiConverterTypeStateUpdateSink = new FfiConverterObject(uniffiTypeStateUpdateSinkObjectFactory); + +/** + * The host-implemented Matrix driver: the same seam as + * [`crate::driver::MatrixDriver`], flattened for FFI. matrix-rust-sdk hosts + * adapt their `MatrixDriver`; a matrix-js-sdk host implements this directly. + * + * Inbound events are a driver job too: the `subscribe_*` methods hand the + * host a sink to emit into. Each is called synchronously, **exactly once**, + * during [`FfiMatrixDriver`] construction — store the sink and hook the + * actual event handlers whenever convenient afterwards. Single-sink + * semantics: fan-out to multiple managers happens on the Rust side. + * + * (`async_trait` must sit *under* the uniffi attribute: uniffi parses the + * original `async fn` tokens.) + */ +export interface MatrixDriverCallback { + + sendStickyEvent(roomId: string, eventType: string, contentJson: string, durationMs: bigint, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; + sendStateEvent(roomId: string, eventType: string, stateKey: string, contentJson: string, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; +/** + * Returns the MSC4140 delay id (not an event id). `sticky_duration_ms` + * makes it a delayed *sticky* event (MSC4354 + MSC4140); ignore it if + * the host SDK cannot express both yet. + */ + sendDelayedEvent(roomId: string, eventType: string, contentJson: string, delayMs: bigint, stickyDurationMs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; +/** + * Delayed state event — compat only (`StateEvents`). + */ + sendDelayedStateEvent(roomId: string, eventType: string, stateKey: string, contentJson: string, delayMs: bigint, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; + restartDelayedEvent(roomId: string, delayId: string, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; + cancelDelayedEvent(roomId: string, delayId: string, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; +/** + * MSC4195: hand the delayed leave to the SFU. `livekit_service_url` is + * the transport we publish on (`None` for a receive-only member) and + * `delay_ms` the armed delay — what an adapter that delegates through + * the authorisation service's token endpoint needs. + */ + delegateLivekitDelayedLeave(roomId: string, slotId: string, memberJson: string, delayId: string, livekitServiceUrl: string | undefined, delayMs: bigint, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; + sendToDevice(recipients: Array, eventType: string, contentJson: string, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise>; +/** + * `GET /_matrix/client/v1/rtc/transports`, with well-known fallback. + */ + getRtcTransports(asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise>; +/** + * MSC4195 token exchange, OpenID token included (see + * [`FfiLivekitTokenRequest`]). + */ + getLivekitToken(request: FfiLivekitTokenRequest, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise; +/** + * Latest `limit` timeline events of `event_type`, as JSON strings. + */ + readEvents(eventType: string, stateKey: string | undefined, limit: number, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise>; +/** + * Current state entries of `event_type` (`state_key: None` = any). + */ + readState(eventType: string, stateKey: string | undefined, asyncOpts_?: { signal: AbortSignal }) /*throws*/: Promise>; +/** + * Live room events (sticky member events, slot/state changes). Store + * the sink; call `emit` from the host SDK's event handlers. + */ + subscribeRoomEvents(sink: RoomEventSinkLike): void; +/** + * Decrypted inbound to-device events with origin metadata. + */ + subscribeToDeviceEvents(sink: ToDeviceSinkLike): void; +/** + * Room-state update batches (what notices a slot closing promptly). + */ + subscribeStateUpdates(sink: StateUpdateSinkLike): void; +/** + * Whether the homeserver is reachable right now (a syncing client: its + * sync loop is running). + */ + isHomeserverConnected(): boolean; +/** + * Live connectivity verdicts. Store the sink; `emit` the new value from + * the host SDK's sync-state handler. + */ + subscribeConnectivity(sink: ConnectivitySinkLike): void; +} + + +/** + * The host-implemented Matrix driver: the same seam as + * [`crate::driver::MatrixDriver`], flattened for FFI. matrix-rust-sdk hosts + * adapt their `MatrixDriver`; a matrix-js-sdk host implements this directly. + * + * Inbound events are a driver job too: the `subscribe_*` methods hand the + * host a sink to emit into. Each is called synchronously, **exactly once**, + * during [`FfiMatrixDriver`] construction — store the sink and hook the + * actual event handlers whenever convenient afterwards. Single-sink + * semantics: fan-out to multiple managers happens on the Rust side. + * + * (`async_trait` must sit *under* the uniffi attribute: uniffi parses the + * original `async fn` tokens.) + */ +export class MatrixDriverCallbackImpl extends UniffiAbstractObject implements MatrixDriverCallback { + + readonly [uniffiTypeNameSymbol] = "MatrixDriverCallbackImpl"; + readonly [destructorGuardSymbol]: UniffiGcObject; + readonly [pointerLiteralSymbol]: UniffiHandle; + // No primary constructor declared for this class. +private constructor(pointer: UniffiHandle) { + super(); + this[pointerLiteralSymbol] = pointer; + this[destructorGuardSymbol] = uniffiTypeMatrixDriverCallbackImplObjectFactory.bless(pointer); +} + + + + + async sendStickyEvent(roomId: string, eventType: string, contentJson: string, durationMs: bigint, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_sticky_event( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterString.lower(roomId, nativeModule().rustbuffer_alloc),FfiConverterString.lower(eventType, nativeModule().rustbuffer_alloc),FfiConverterString.lower(contentJson, nativeModule().rustbuffer_alloc),FfiConverterUInt64.lower(durationMs, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer, + // Async returns always go through the JS-side converter: the + // FFI symbol returns the future handle (u64), and the user-level + // RustBuffer comes back via the shared `rust_future_complete_*` + // export. The bytes the runtime hands back must be deserialized + // here using the per-callable return-type converter. + // Borrowed view over foreign memory: the call site owns the free, + // as on the sync paths. Unconditional — a no-op where buffers are + // already JS-owned. + /*liftFunc:*/ (__rb) => { + try { + return FfiConverterTypeFfiSendEventResponse.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + + async sendStateEvent(roomId: string, eventType: string, stateKey: string, contentJson: string, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_state_event( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterString.lower(roomId, nativeModule().rustbuffer_alloc),FfiConverterString.lower(eventType, nativeModule().rustbuffer_alloc),FfiConverterString.lower(stateKey, nativeModule().rustbuffer_alloc),FfiConverterString.lower(contentJson, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer, + // Async returns always go through the JS-side converter: the + // FFI symbol returns the future handle (u64), and the user-level + // RustBuffer comes back via the shared `rust_future_complete_*` + // export. The bytes the runtime hands back must be deserialized + // here using the per-callable return-type converter. + // Borrowed view over foreign memory: the call site owns the free, + // as on the sync paths. Unconditional — a no-op where buffers are + // already JS-owned. + /*liftFunc:*/ (__rb) => { + try { + return FfiConverterTypeFfiSendEventResponse.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * Returns the MSC4140 delay id (not an event id). `sticky_duration_ms` + * makes it a delayed *sticky* event (MSC4354 + MSC4140); ignore it if + * the host SDK cannot express both yet. + */ + async sendDelayedEvent(roomId: string, eventType: string, contentJson: string, delayMs: bigint, stickyDurationMs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_delayed_event( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterString.lower(roomId, nativeModule().rustbuffer_alloc),FfiConverterString.lower(eventType, nativeModule().rustbuffer_alloc),FfiConverterString.lower(contentJson, nativeModule().rustbuffer_alloc),FfiConverterUInt64.lower(delayMs, nativeModule().rustbuffer_alloc),FfiConverterOptionalUInt64.lower(stickyDurationMs, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer, + // Async returns always go through the JS-side converter: the + // FFI symbol returns the future handle (u64), and the user-level + // RustBuffer comes back via the shared `rust_future_complete_*` + // export. The bytes the runtime hands back must be deserialized + // here using the per-callable return-type converter. + // Borrowed view over foreign memory: the call site owns the free, + // as on the sync paths. Unconditional — a no-op where buffers are + // already JS-owned. + /*liftFunc:*/ (__rb) => { + try { + return FfiConverterString.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * Delayed state event — compat only (`StateEvents`). + */ + async sendDelayedStateEvent(roomId: string, eventType: string, stateKey: string, contentJson: string, delayMs: bigint, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_delayed_state_event( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterString.lower(roomId, nativeModule().rustbuffer_alloc),FfiConverterString.lower(eventType, nativeModule().rustbuffer_alloc),FfiConverterString.lower(stateKey, nativeModule().rustbuffer_alloc),FfiConverterString.lower(contentJson, nativeModule().rustbuffer_alloc),FfiConverterUInt64.lower(delayMs, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer, + // Async returns always go through the JS-side converter: the + // FFI symbol returns the future handle (u64), and the user-level + // RustBuffer comes back via the shared `rust_future_complete_*` + // export. The bytes the runtime hands back must be deserialized + // here using the per-callable return-type converter. + // Borrowed view over foreign memory: the call site owns the free, + // as on the sync paths. Unconditional — a no-op where buffers are + // already JS-owned. + /*liftFunc:*/ (__rb) => { + try { + return FfiConverterString.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + + async restartDelayedEvent(roomId: string, delayId: string, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_restart_delayed_event( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterString.lower(roomId, nativeModule().rustbuffer_alloc),FfiConverterString.lower(delayId, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_void, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_void, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_void, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_void, + /*liftFunc:*/ (_v) => {}, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + + async cancelDelayedEvent(roomId: string, delayId: string, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_cancel_delayed_event( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterString.lower(roomId, nativeModule().rustbuffer_alloc),FfiConverterString.lower(delayId, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_void, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_void, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_void, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_void, + /*liftFunc:*/ (_v) => {}, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * MSC4195: hand the delayed leave to the SFU. `livekit_service_url` is + * the transport we publish on (`None` for a receive-only member) and + * `delay_ms` the armed delay — what an adapter that delegates through + * the authorisation service's token endpoint needs. + */ + async delegateLivekitDelayedLeave(roomId: string, slotId: string, memberJson: string, delayId: string, livekitServiceUrl: string | undefined, delayMs: bigint, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_delegate_livekit_delayed_leave( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterString.lower(roomId, nativeModule().rustbuffer_alloc),FfiConverterString.lower(slotId, nativeModule().rustbuffer_alloc),FfiConverterString.lower(memberJson, nativeModule().rustbuffer_alloc),FfiConverterString.lower(delayId, nativeModule().rustbuffer_alloc),FfiConverterOptionalString.lower(livekitServiceUrl, nativeModule().rustbuffer_alloc),FfiConverterUInt64.lower(delayMs, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_void, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_void, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_void, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_void, + /*liftFunc:*/ (_v) => {}, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + + async sendToDevice(recipients: Array, eventType: string, contentJson: string, asyncOpts_?: { signal: AbortSignal }): Promise> /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_to_device( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterSequenceTypeFfiToDeviceRecipient.lower(recipients, nativeModule().rustbuffer_alloc),FfiConverterString.lower(eventType, nativeModule().rustbuffer_alloc),FfiConverterString.lower(contentJson, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer, + // Async returns always go through the JS-side converter: the + // FFI symbol returns the future handle (u64), and the user-level + // RustBuffer comes back via the shared `rust_future_complete_*` + // export. The bytes the runtime hands back must be deserialized + // here using the per-callable return-type converter. + // Borrowed view over foreign memory: the call site owns the free, + // as on the sync paths. Unconditional — a no-op where buffers are + // already JS-owned. + /*liftFunc:*/ (__rb) => { + try { + return FfiConverterSequenceTypeFfiToDeviceDelivery.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * `GET /_matrix/client/v1/rtc/transports`, with well-known fallback. + */ + async getRtcTransports(asyncOpts_?: { signal: AbortSignal }): Promise> /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_get_rtc_transports( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer, + // Async returns always go through the JS-side converter: the + // FFI symbol returns the future handle (u64), and the user-level + // RustBuffer comes back via the shared `rust_future_complete_*` + // export. The bytes the runtime hands back must be deserialized + // here using the per-callable return-type converter. + // Borrowed view over foreign memory: the call site owns the free, + // as on the sync paths. Unconditional — a no-op where buffers are + // already JS-owned. + /*liftFunc:*/ (__rb) => { + try { + return FfiConverterSequenceTypeFfiRtcTransport.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * MSC4195 token exchange, OpenID token included (see + * [`FfiLivekitTokenRequest`]). + */ + async getLivekitToken(request: FfiLivekitTokenRequest, asyncOpts_?: { signal: AbortSignal }): Promise /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_get_livekit_token( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterTypeFfiLivekitTokenRequest.lower(request, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer, + // Async returns always go through the JS-side converter: the + // FFI symbol returns the future handle (u64), and the user-level + // RustBuffer comes back via the shared `rust_future_complete_*` + // export. The bytes the runtime hands back must be deserialized + // here using the per-callable return-type converter. + // Borrowed view over foreign memory: the call site owns the free, + // as on the sync paths. Unconditional — a no-op where buffers are + // already JS-owned. + /*liftFunc:*/ (__rb) => { + try { + return FfiConverterTypeFfiLivekitToken.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * Latest `limit` timeline events of `event_type`, as JSON strings. + */ + async readEvents(eventType: string, stateKey: string | undefined, limit: number, asyncOpts_?: { signal: AbortSignal }): Promise> /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_read_events( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterString.lower(eventType, nativeModule().rustbuffer_alloc),FfiConverterOptionalString.lower(stateKey, nativeModule().rustbuffer_alloc),FfiConverterUInt32.lower(limit, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer, + // Async returns always go through the JS-side converter: the + // FFI symbol returns the future handle (u64), and the user-level + // RustBuffer comes back via the shared `rust_future_complete_*` + // export. The bytes the runtime hands back must be deserialized + // here using the per-callable return-type converter. + // Borrowed view over foreign memory: the call site owns the free, + // as on the sync paths. Unconditional — a no-op where buffers are + // already JS-owned. + /*liftFunc:*/ (__rb) => { + try { + return FfiConverterSequenceString.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * Current state entries of `event_type` (`state_key: None` = any). + */ + async readState(eventType: string, stateKey: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> /*throws*/ { + return await uniffiRustCallAsync( + /*rustCaller:*/ uniffiCaller, + /*rustFutureFunc:*/ () => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_read_state( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this),FfiConverterString.lower(eventType, nativeModule().rustbuffer_alloc),FfiConverterOptionalString.lower(stateKey, nativeModule().rustbuffer_alloc) + ); + }, + /*pollFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer, + /*cancelFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer, + /*completeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer, + /*freeFunc:*/ nativeModule().ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer, + // Async returns always go through the JS-side converter: the + // FFI symbol returns the future handle (u64), and the user-level + // RustBuffer comes back via the shared `rust_future_complete_*` + // export. The bytes the runtime hands back must be deserialized + // here using the per-callable return-type converter. + // Borrowed view over foreign memory: the call site owns the free, + // as on the sync paths. Unconditional — a no-op where buffers are + // already JS-owned. + /*liftFunc:*/ (__rb) => { + try { + return FfiConverterSequenceString.lift(__rb); + } finally { + nativeModule().rustbuffer_free(__rb); + } + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + /*asyncOpts:*/ asyncOpts_, + /*errorHandler:*/ FfiConverterTypeRtcError.lift.bind(FfiConverterTypeRtcError) + ); + } + +/** + * Live room events (sticky member events, slot/state changes). Store + * the sink; call `emit` from the host SDK's event handlers. + */ + subscribeRoomEvents(sink: RoomEventSinkLike): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_room_events( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this), + FfiConverterTypeRoomEventSink.lower(sink, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + +/** + * Decrypted inbound to-device events with origin metadata. + */ + subscribeToDeviceEvents(sink: ToDeviceSinkLike): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_to_device_events( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this), + FfiConverterTypeToDeviceSink.lower(sink, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + +/** + * Room-state update batches (what notices a slot closing promptly). + */ + subscribeStateUpdates(sink: StateUpdateSinkLike): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_state_updates( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this), + FfiConverterTypeStateUpdateSink.lower(sink, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + +/** + * Whether the homeserver is reachable right now (a syncing client: its + * sync loop is running). + */ + isHomeserverConnected(): boolean { + return FfiConverterBool.lift(uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { + return nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_is_homeserver_connected( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + )); + } + +/** + * Live connectivity verdicts. Store the sink; `emit` the new value from + * the host SDK's sync-state handler. + */ + subscribeConnectivity(sink: ConnectivitySinkLike): void {uniffiCaller.rustCall( + /*caller:*/ (callStatus) => { nativeModule().ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_connectivity( + uniffiTypeMatrixDriverCallbackImplObjectFactory.clonePointer(this), + FfiConverterTypeConnectivitySink.lower(sink, nativeModule().rustbuffer_alloc), + callStatus); + }, + /*liftString:*/ FfiConverterString.lift.bind(FfiConverterString), + ); + } + + + uniffiDestroy(): void { + const ptr = (this as any)[destructorGuardSymbol]; + if (ptr !== undefined) { + const pointer = uniffiTypeMatrixDriverCallbackImplObjectFactory.pointer(this); + uniffiTypeMatrixDriverCallbackImplObjectFactory.freePointer(pointer); + uniffiTypeMatrixDriverCallbackImplObjectFactory.unbless(ptr); + delete (this as any)[destructorGuardSymbol]; + } + } + + static instanceOf(obj_: any): obj_ is MatrixDriverCallbackImpl { + return uniffiTypeMatrixDriverCallbackImplObjectFactory.isConcreteType(obj_); + } + + +} + +const uniffiTypeMatrixDriverCallbackImplObjectFactory: UniffiObjectFactory = (() => { + + /// + const registry = typeof FinalizationRegistry !== 'undefined' ? new FinalizationRegistry((heldValue: UniffiHandle) => { + uniffiTypeMatrixDriverCallbackImplObjectFactory.freePointer(heldValue); + }) : null; + + return { + create(pointer: UniffiHandle): MatrixDriverCallback { + const instance = Object.create(MatrixDriverCallbackImpl.prototype); + instance[pointerLiteralSymbol] = pointer; + instance[destructorGuardSymbol] = this.bless(pointer); + instance[uniffiTypeNameSymbol] = "MatrixDriverCallbackImpl"; + return instance; + }, + + + bless(p: UniffiHandle): UniffiGcObject { + const ptr = { + p, // make sure this object doesn't get optimized away. + markDestroyed: () => undefined, + }; + if (registry) { + registry.register(ptr, p, ptr); + } + return ptr; + }, + + unbless(ptr_: UniffiGcObject) { + if (registry) { + registry.unregister(ptr_); + } + }, + + pointer(obj_: MatrixDriverCallback): UniffiHandle { + if ((obj_ as any)[destructorGuardSymbol] === undefined) { + throw new UniffiInternalError.UnexpectedNullPointer(); + } + return (obj_ as any)[pointerLiteralSymbol]; + }, + + clonePointer(obj_: MatrixDriverCallback): UniffiHandle { + const pointer = this.pointer(obj_); + return uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_clone_matrixdrivercallback(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + freePointer(pointer: UniffiHandle): void { + uniffiCaller.rustCall( + /*caller:*/ (callStatus) => nativeModule().ubrn_uniffi_matrix_rtc_fn_free_matrixdrivercallback(pointer, callStatus), + /*liftString:*/ FfiConverterString.lift + ); + }, + + isConcreteType(obj_: any): obj_ is MatrixDriverCallback { + return obj_[destructorGuardSymbol] && obj_[uniffiTypeNameSymbol] === "MatrixDriverCallbackImpl"; + }, +}})(); +const FfiConverterTypeMatrixDriverCallback = new FfiConverterObjectWithCallbacks(uniffiTypeMatrixDriverCallbackImplObjectFactory); + +// Add a vtable for the callbacks that go in MatrixDriverCallback. + +// Put the implementation in a struct so we don't pollute the top-level namespace +const uniffiCallbackInterfaceMatrixDriverCallback: { vtable: any; register: () => void; } = { + // Create the VTable using a series of closures. + // ts automatically converts these into C callback functions. + vtable: { + send_sticky_event: ( + uniffiHandle: bigint, + roomId: Uint8Array, + eventType: Uint8Array, + contentJson: Uint8Array, + durationMs: bigint, + uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.sendStickyEvent( + FfiConverterString.lift(roomId), + FfiConverterString.lift(eventType), + FfiConverterString.lift(contentJson), + FfiConverterUInt64.lift(durationMs), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: FfiSendEventResponse) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: FfiConverterTypeFfiSendEventResponse.lower(returnValue, nativeModule().rustbuffer_alloc), + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: /*empty*/ new Uint8Array(0), + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + send_state_event: ( + uniffiHandle: bigint, + roomId: Uint8Array, + eventType: Uint8Array, + stateKey: Uint8Array, + contentJson: Uint8Array, + uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.sendStateEvent( + FfiConverterString.lift(roomId), + FfiConverterString.lift(eventType), + FfiConverterString.lift(stateKey), + FfiConverterString.lift(contentJson), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: FfiSendEventResponse) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: FfiConverterTypeFfiSendEventResponse.lower(returnValue, nativeModule().rustbuffer_alloc), + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: /*empty*/ new Uint8Array(0), + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + send_delayed_event: ( + uniffiHandle: bigint, + roomId: Uint8Array, + eventType: Uint8Array, + contentJson: Uint8Array, + delayMs: bigint, + stickyDurationMs: Uint8Array, + uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.sendDelayedEvent( + FfiConverterString.lift(roomId), + FfiConverterString.lift(eventType), + FfiConverterString.lift(contentJson), + FfiConverterUInt64.lift(delayMs), + FfiConverterOptionalUInt64.lift(stickyDurationMs), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: string) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: FfiConverterString.lower(returnValue, nativeModule().rustbuffer_alloc), + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: /*empty*/ new Uint8Array(0), + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + send_delayed_state_event: ( + uniffiHandle: bigint, + roomId: Uint8Array, + eventType: Uint8Array, + stateKey: Uint8Array, + contentJson: Uint8Array, + delayMs: bigint, + uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.sendDelayedStateEvent( + FfiConverterString.lift(roomId), + FfiConverterString.lift(eventType), + FfiConverterString.lift(stateKey), + FfiConverterString.lift(contentJson), + FfiConverterUInt64.lift(delayMs), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: string) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: FfiConverterString.lower(returnValue, nativeModule().rustbuffer_alloc), + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: /*empty*/ new Uint8Array(0), + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + restart_delayed_event: ( + uniffiHandle: bigint, + roomId: Uint8Array, + delayId: Uint8Array, + uniffiFutureCallback: UniffiForeignFutureCompletevoid, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.restartDelayedEvent( + FfiConverterString.lift(roomId), + FfiConverterString.lift(delayId), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: void) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultVoid */{ + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultVoid */{ + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + cancel_delayed_event: ( + uniffiHandle: bigint, + roomId: Uint8Array, + delayId: Uint8Array, + uniffiFutureCallback: UniffiForeignFutureCompletevoid, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.cancelDelayedEvent( + FfiConverterString.lift(roomId), + FfiConverterString.lift(delayId), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: void) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultVoid */{ + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultVoid */{ + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + delegate_livekit_delayed_leave: ( + uniffiHandle: bigint, + roomId: Uint8Array, + slotId: Uint8Array, + memberJson: Uint8Array, + delayId: Uint8Array, + livekitServiceUrl: Uint8Array, + delayMs: bigint, + uniffiFutureCallback: UniffiForeignFutureCompletevoid, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.delegateLivekitDelayedLeave( + FfiConverterString.lift(roomId), + FfiConverterString.lift(slotId), + FfiConverterString.lift(memberJson), + FfiConverterString.lift(delayId), + FfiConverterOptionalString.lift(livekitServiceUrl), + FfiConverterUInt64.lift(delayMs), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: void) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultVoid */{ + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultVoid */{ + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + send_to_device: ( + uniffiHandle: bigint, + recipients: Uint8Array, + eventType: Uint8Array, + contentJson: Uint8Array, + uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise> => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.sendToDevice( + FfiConverterSequenceTypeFfiToDeviceRecipient.lift(recipients), + FfiConverterString.lift(eventType), + FfiConverterString.lift(contentJson), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: Array) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: FfiConverterSequenceTypeFfiToDeviceDelivery.lower(returnValue, nativeModule().rustbuffer_alloc), + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: /*empty*/ new Uint8Array(0), + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + get_rtc_transports: ( + uniffiHandle: bigint, + uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise> => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.getRtcTransports({ signal } + ) + }; + const uniffiHandleSuccess = (returnValue: Array) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: FfiConverterSequenceTypeFfiRtcTransport.lower(returnValue, nativeModule().rustbuffer_alloc), + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: /*empty*/ new Uint8Array(0), + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + get_livekit_token: ( + uniffiHandle: bigint, + request: Uint8Array, + uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.getLivekitToken( + FfiConverterTypeFfiLivekitTokenRequest.lift(request), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: FfiLivekitToken) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: FfiConverterTypeFfiLivekitToken.lower(returnValue, nativeModule().rustbuffer_alloc), + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: /*empty*/ new Uint8Array(0), + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + read_events: ( + uniffiHandle: bigint, + eventType: Uint8Array, + stateKey: Uint8Array, + limit: number, + uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise> => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.readEvents( + FfiConverterString.lift(eventType), + FfiConverterOptionalString.lift(stateKey), + FfiConverterUInt32.lift(limit), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: Array) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: FfiConverterSequenceString.lower(returnValue, nativeModule().rustbuffer_alloc), + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: /*empty*/ new Uint8Array(0), + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + read_state: ( + uniffiHandle: bigint, + eventType: Uint8Array, + stateKey: Uint8Array, + uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, + uniffiCallbackData: bigint) => { + const uniffiMakeCall = + async (signal: AbortSignal) + : Promise> => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return await jsCallback.readState( + FfiConverterString.lift(eventType), + FfiConverterOptionalString.lift(stateKey), { signal } + ) + }; + const uniffiHandleSuccess = (returnValue: Array) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: FfiConverterSequenceString.lower(returnValue, nativeModule().rustbuffer_alloc), + call_status: uniffiCaller.createCallStatus() + } + ); + }; + const uniffiHandleError = (code: number, errorBuf: UniffiByteArray) => { + uniffiFutureCallback.call( + uniffiFutureCallback, + uniffiCallbackData, + /* UniffiForeignFutureResultRustBuffer */{ + return_value: /*empty*/ new Uint8Array(0), + // TODO create callstatus with error. + call_status: uniffiCaller.createErrorStatus(code, errorBuf), + } + ); + }; + const uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*isErrorType:*/ RtcError.instanceOf, + /*lowerError:*/ FfiConverterTypeRtcError.lower.bind(FfiConverterTypeRtcError), + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ); + return uniffiForeignFuture; + }, + subscribe_room_events: ( + uniffiHandle: bigint, + sink: bigint,) => { + const uniffiMakeCall = + () + : void => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return jsCallback.subscribeRoomEvents( + FfiConverterTypeRoomEventSink.lift(sink) + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => {}; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + subscribe_to_device_events: ( + uniffiHandle: bigint, + sink: bigint,) => { + const uniffiMakeCall = + () + : void => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return jsCallback.subscribeToDeviceEvents( + FfiConverterTypeToDeviceSink.lift(sink) + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => {}; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + subscribe_state_updates: ( + uniffiHandle: bigint, + sink: bigint,) => { + const uniffiMakeCall = + () + : void => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return jsCallback.subscribeStateUpdates( + FfiConverterTypeStateUpdateSink.lift(sink) + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => {}; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + is_homeserver_connected: ( + uniffiHandle: bigint,) => { + const uniffiMakeCall = + () + : boolean => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return jsCallback.isHomeserverConnected( + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => { + UniffiResult.writeSuccess(uniffiResult, FfiConverterBool.lower(obj, nativeModule().rustbuffer_alloc)); + }; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + subscribe_connectivity: ( + uniffiHandle: bigint, + sink: bigint,) => { + const uniffiMakeCall = + () + : void => { + const jsCallback = FfiConverterTypeMatrixDriverCallback.lift(uniffiHandle); + return jsCallback.subscribeConnectivity( + FfiConverterTypeConnectivitySink.lift(sink) + ) + }; + const uniffiResult = UniffiResult.ready(); + const uniffiHandleSuccess = (obj: any) => {}; + const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => { + UniffiResult.writeError(uniffiResult, code, errBuf); + }; + uniffiTraitInterfaceCall( + /*makeCall:*/ uniffiMakeCall, + /*handleSuccess:*/ uniffiHandleSuccess, + /*handleError:*/ uniffiHandleError, + /*lowerString:*/ FfiConverterString.lower.bind(FfiConverterString), + /*alloc:*/ nativeModule().rustbuffer_alloc, + ) + return uniffiResult; + }, + uniffi_free: (uniffiHandle: UniffiHandle): void => { + // this will throw a stale handle error if the handle isn't found. + FfiConverterTypeMatrixDriverCallback.drop(uniffiHandle); + }, + uniffi_clone: (uniffiHandle: UniffiHandle): UniffiHandle => { + return FfiConverterTypeMatrixDriverCallback.clone(uniffiHandle); + } + }, + register: () => {nativeModule().ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_matrixdrivercallback( + uniffiCallbackInterfaceMatrixDriverCallback.vtable + ); + }, +}; + +// FfiConverter for bigint | undefined +const FfiConverterOptionalUInt64 = new FfiConverterOptional(FfiConverterUInt64); + +// FfiConverter for Array +const FfiConverterSequenceString = new FfiConverterArray(FfiConverterString); + +// FfiConverter for string | undefined +const FfiConverterOptionalString = new FfiConverterOptional(FfiConverterString); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiRtcTransport = new FfiConverterArray(FfiConverterTypeFfiRtcTransport); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiMember = new FfiConverterArray(FfiConverterTypeFfiMember); + +// FfiConverter for FfiKeyRejection | undefined +const FfiConverterOptionalTypeFfiKeyRejection = new FfiConverterOptional(FfiConverterTypeFfiKeyRejection); + +// FfiConverter for FfiMediaKeyState | undefined +const FfiConverterOptionalTypeFfiMediaKeyState = new FfiConverterOptional(FfiConverterTypeFfiMediaKeyState); + +// FfiConverter for boolean | undefined +const FfiConverterOptionalBoolean = new FfiConverterOptional(FfiConverterBool); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiSessionRead = new FfiConverterArray(FfiConverterTypeFfiSessionRead); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiExcludedCandidate = new FfiConverterArray(FfiConverterTypeFfiExcludedCandidate); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiImpairment = new FfiConverterArray(FfiConverterTypeFfiImpairment); + +// FfiConverter for FfiDelayedLeaveOutcome | undefined +const FfiConverterOptionalTypeFfiDelayedLeaveOutcome = new FfiConverterOptional(FfiConverterTypeFfiDelayedLeaveOutcome); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiConnectionWithMembers = new FfiConverterArray(FfiConverterTypeFfiConnectionWithMembers); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiConnectionProblem = new FfiConverterArray(FfiConverterTypeFfiConnectionProblem); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiMediaKey = new FfiConverterArray(FfiConverterTypeFfiMediaKey); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiMembership = new FfiConverterArray(FfiConverterTypeFfiMembership); + +// FfiConverter for FfiMembership | undefined +const FfiConverterOptionalTypeFfiMembership = new FfiConverterOptional(FfiConverterTypeFfiMembership); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiToDeviceRecipient = new FfiConverterArray(FfiConverterTypeFfiToDeviceRecipient); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiToDeviceDelivery = new FfiConverterArray(FfiConverterTypeFfiToDeviceDelivery); + +// FfiConverter for Array +const FfiConverterSequenceTypeFfiSessionSnapshot = new FfiConverterArray(FfiConverterTypeFfiSessionSnapshot); + + +/** + * This should be called before anything else. + * + * It is likely that this is being done for you by the library's `index.ts`. + * + * It checks versions of uniffi between when the Rust scaffolding was generated + * and when the bindings were generated. + * + * It also initializes the machinery to enable Rust to talk back to Javascript. + */ +function uniffiEnsureInitialized() { + // Get the bindings contract version from our ComponentInterface + const bindingsContractVersion = 30; + // Get the scaffolding contract version by calling the into the dylib + const scaffoldingContractVersion = nativeModule().ubrn_ffi_matrix_rtc_uniffi_contract_version(); + if (bindingsContractVersion !== scaffoldingContractVersion) { + throw new UniffiInternalError.ContractVersionMismatch(scaffoldingContractVersion, bindingsContractVersion); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_func_compute_sessions_from_events() !== 27010) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_func_compute_sessions_from_events"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_func_impairment_severity() !== 9139) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_func_impairment_severity"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_connectionslistener_on_connections_change() !== 24219) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_connectionslistener_on_connections_change"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_connectivitysink_emit() !== 45059) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_connectivitysink_emit"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_constructor_ffimatrixdriver_new() !== 16380) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_constructor_ffimatrixdriver_new"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_constructor_ffiparticipationmanager_new() !== 2422) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_constructor_ffiparticipationmanager_new"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_close_slot() !== 63268) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_close_slot"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_connection_problems() !== 17464) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_connection_problems"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_connections() !== 38871) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_connections"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_debug_snapshot() !== 11860) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_debug_snapshot"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_is_homeserver_connected() !== 218) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_is_homeserver_connected"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_join() !== 18117) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_join"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_key_map() !== 32581) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_key_map"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_leave() !== 35402) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_leave"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_memberships() !== 15145) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_memberships"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_open_slot() !== 28952) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_open_slot"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_member_id() !== 453) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_member_id"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_membership() !== 25006) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_membership"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_transport_identity() !== 34101) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_transport_identity"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_session() !== 13049) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_session"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_connections_listener() !== 33842) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_connections_listener"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_key_map_listener() !== 53147) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_key_map_listener"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_key_rejected_listener() !== 1468) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_key_rejected_listener"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_memberships_listener() !== 46727) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_memberships_listener"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_status_listener() !== 8439) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_status_listener"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_status() !== 23619) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_status"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_update_application() !== 28805) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_update_application"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_keymaplistener_on_key_map_change() !== 2308) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_keymaplistener_on_key_map_change"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_keyrejectedlistener_on_key_rejected() !== 53044) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_keyrejectedlistener_on_key_rejected"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_sticky_event() !== 39725) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_sticky_event"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_state_event() !== 11746) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_state_event"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_delayed_event() !== 9018) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_delayed_event"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_delayed_state_event() !== 29945) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_delayed_state_event"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_restart_delayed_event() !== 2361) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_restart_delayed_event"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_cancel_delayed_event() !== 48021) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_cancel_delayed_event"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_delegate_livekit_delayed_leave() !== 31950) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_delegate_livekit_delayed_leave"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_to_device() !== 29274) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_to_device"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_get_rtc_transports() !== 55674) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_get_rtc_transports"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_get_livekit_token() !== 36238) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_get_livekit_token"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_read_events() !== 18104) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_read_events"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_read_state() !== 58428) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_read_state"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_room_events() !== 31129) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_room_events"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_to_device_events() !== 55587) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_to_device_events"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_state_updates() !== 53493) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_state_updates"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_is_homeserver_connected() !== 28631) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_is_homeserver_connected"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_connectivity() !== 46098) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_connectivity"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_membershipslistener_on_memberships_change() !== 29319) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_membershipslistener_on_memberships_change"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_roomeventsink_emit() !== 34604) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_roomeventsink_emit"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_stateupdatesink_emit() !== 7988) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_stateupdatesink_emit"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_statuslistener_on_status_change() !== 45246) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_statuslistener_on_status_change"); + } + if (nativeModule().ubrn_uniffi_matrix_rtc_checksum_method_todevicesink_emit() !== 57061) { + throw new UniffiInternalError.ApiChecksumMismatch("uniffi_matrix_rtc_checksum_method_todevicesink_emit"); + } + + uniffiCallbackInterfaceConnectionsListener.register(); + uniffiCallbackInterfaceKeyMapListener.register(); + uniffiCallbackInterfaceKeyRejectedListener.register(); + uniffiCallbackInterfaceMembershipsListener.register(); + uniffiCallbackInterfaceStatusListener.register(); + uniffiCallbackInterfaceMatrixDriverCallback.register(); + } + +export default Object.freeze({ + initialize: uniffiEnsureInitialized, + converters: { + FfiConverterTypeConnectionsListener, + FfiConverterTypeConnectivitySink, + FfiConverterTypeFfiComponent, + FfiConverterTypeFfiConnectionData, + FfiConverterTypeFfiConnectionProblem, + FfiConverterTypeFfiConnectionProblemKind, + FfiConverterTypeFfiConnectionWithMembers, + FfiConverterTypeFfiDelayedLeaveOutcome, + FfiConverterTypeFfiDeviceAttribution, + FfiConverterTypeFfiDisconnectCause, + FfiConverterTypeFfiElementCallCompat, + FfiConverterTypeFfiEncryptionStatus, + FfiConverterTypeFfiEventOrigin, + FfiConverterTypeFfiExcludedCandidate, + FfiConverterTypeFfiImpairment, + FfiConverterTypeFfiJoinError, + FfiConverterTypeFfiJoinExclusionReason, + FfiConverterTypeFfiJoinParams, + FfiConverterTypeFfiJoinProgress, + FfiConverterTypeFfiKeepAlive, + FfiConverterTypeFfiKeyRejection, + FfiConverterTypeFfiLivekitToken, + FfiConverterTypeFfiLivekitTokenRequest, + FfiConverterTypeFfiMatrixDriver, + FfiConverterTypeFfiMediaKey, + FfiConverterTypeFfiMediaKeyState, + FfiConverterTypeFfiMember, + FfiConverterTypeFfiMembership, + FfiConverterTypeFfiMembershipPublication, + FfiConverterTypeFfiMembershipState, + FfiConverterTypeFfiParticipationConfig, + FfiConverterTypeFfiParticipationManager, + FfiConverterTypeFfiRosterPresence, + FfiConverterTypeFfiRtcTransport, + FfiConverterTypeFfiSendEventResponse, + FfiConverterTypeFfiSessionRead, + FfiConverterTypeFfiSessionSnapshot, + FfiConverterTypeFfiSeverity, + FfiConverterTypeFfiStatus, + FfiConverterTypeFfiToDeviceDelivery, + FfiConverterTypeFfiToDeviceRecipient, + FfiConverterTypeFfiTransportIntent, + FfiConverterTypeKeyMapListener, + FfiConverterTypeKeyRejectedListener, + FfiConverterTypeMatrixDriverCallback, + FfiConverterTypeMembershipsListener, + FfiConverterTypeRoomEventSink, + FfiConverterTypeRtcError, + FfiConverterTypeStateUpdateSink, + FfiConverterTypeStatusListener, + FfiConverterTypeToDeviceSink, + } +}); \ No newline at end of file diff --git a/src/matrix-rtc-sdk/generated/wasm-bindgen/index.d.ts b/src/matrix-rtc-sdk/generated/wasm-bindgen/index.d.ts new file mode 100644 index 000000000..204a74eea --- /dev/null +++ b/src/matrix-rtc-sdk/generated/wasm-bindgen/index.d.ts @@ -0,0 +1,30 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +// Hand-written declaration for the wasm-bindgen glue next to it, which ubrn +// emits as plain JavaScript. Only the loading entry points are declared; the +// exported `ubrn_*` FFI functions are reached through matrix_rtc.ts alone. +// Kept out of the sync script's way: it is not generated. + +/** What wasm-bindgen's loader accepts as the module to instantiate. */ +export type InitInput = + | RequestInfo + | URL + | Response + | BufferSource + | WebAssembly.Module; + +export default function initAsync( + moduleOrPath?: + | { module_or_path?: InitInput | Promise } + | InitInput + | Promise, +): Promise; + +export function initSync( + module: { module: BufferSource | WebAssembly.Module } | BufferSource | WebAssembly.Module, +): unknown; diff --git a/src/matrix-rtc-sdk/generated/wasm-bindgen/index.js b/src/matrix-rtc-sdk/generated/wasm-bindgen/index.js new file mode 100644 index 000000000..3692c76d9 --- /dev/null +++ b/src/matrix-rtc-sdk/generated/wasm-bindgen/index.js @@ -0,0 +1,3049 @@ +let wasm; + +let cachedUint8ArrayMemory0 = null; + +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); +} + +function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_export_3.set(idx, obj); + return idx; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); + } +} + +function _assertClass(instance, klass) { + if (!(instance instanceof klass)) { + throw new Error(`expected instance of ${klass.name}`); + } +} + +let WASM_VECTOR_LEN = 0; + +function passArray8ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 1, 1) >>> 0; + getUint8ArrayMemory0().set(arg, ptr / 1); + WASM_VECTOR_LEN = arg.length; + return ptr; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let cachedDataViewMemory0 = null; + +function getDataViewMemory0() { + if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { + cachedDataViewMemory0 = new DataView(wasm.memory.buffer); + } + return cachedDataViewMemory0; +} + +const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); + +if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(state => { + wasm.__wbindgen_export_5.get(state.dtor)(state.a, state.b) +}); + +function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + if (--state.cnt === 0) { + wasm.__wbindgen_export_5.get(state.dtor)(a, state.b); + CLOSURE_DTORS.unregister(state); + } else { + state.a = a; + } + } + }; + real.original = state; + CLOSURE_DTORS.register(real, state, state); + return real; +} +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_connectionslistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_connectionslistener(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_memberships(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_memberships(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} application_type + * @param {number} encrypted + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_open_slot(ptr, application_type, encrypted) { + const ptr0 = passArray8ToWasm0(application_type, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_open_slot(ptr, ptr0, len0, encrypted); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_own_member_id(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_own_member_id(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} intent + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_update_application(ptr, intent) { + const ptr0 = passArray8ToWasm0(intent, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_update_application(ptr, ptr0, len0); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_keymaplistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_keymaplistener(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_keymaplistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_keymaplistener(handle, f_status_.__wbg_ptr); +} + +/** + * @param {any} vtable + */ +export function ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_keymaplistener(vtable) { + wasm.ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_keymaplistener(vtable); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} key_map + * @param {Uint8Array} change + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_keymaplistener_on_key_map_change(ptr, key_map, change, f_status_) { + const ptr0 = passArray8ToWasm0(key_map, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(change, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_keymaplistener_on_key_map_change(ptr, ptr0, len0, ptr1, len1, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_keyrejectedlistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_keyrejectedlistener(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_keyrejectedlistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_keyrejectedlistener(handle, f_status_.__wbg_ptr); +} + +/** + * @param {any} vtable + */ +export function ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_keyrejectedlistener(vtable) { + wasm.ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_keyrejectedlistener(vtable); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} member_id + * @param {Uint8Array} reason + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_keyrejectedlistener_on_key_rejected(ptr, member_id, reason, f_status_) { + const ptr0 = passArray8ToWasm0(member_id, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(reason, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_keyrejectedlistener_on_key_rejected(ptr, ptr0, len0, ptr1, len1, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_own_membership(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_own_membership(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_own_transport_identity(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_own_transport_identity(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_session(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_session(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} ptr + * @param {bigint} listener + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_connections_listener(ptr, listener, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_connections_listener(ptr, listener, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {bigint} listener + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_key_map_listener(ptr, listener, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_key_map_listener(ptr, listener, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {bigint} listener + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_key_rejected_listener(ptr, listener, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_key_rejected_listener(ptr, listener, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {bigint} listener + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_memberships_listener(ptr, listener, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_memberships_listener(ptr, listener, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {bigint} listener + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_status_listener(ptr, listener, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_set_status_listener(ptr, listener, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_status(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_status(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_matrixdrivercallback(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_matrixdrivercallback(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_matrixdrivercallback(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_matrixdrivercallback(handle, f_status_.__wbg_ptr); +} + +/** + * @param {any} vtable + */ +export function ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_matrixdrivercallback(vtable) { + wasm.ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_matrixdrivercallback(vtable); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} request + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_get_livekit_token(ptr, request) { + const ptr0 = passArray8ToWasm0(request, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_get_livekit_token(ptr, ptr0, len0); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} event_type + * @param {Uint8Array} state_key + * @param {number} limit + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_read_events(ptr, event_type, state_key, limit) { + const ptr0 = passArray8ToWasm0(event_type, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(state_key, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_read_events(ptr, ptr0, len0, ptr1, len1, limit); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} event_type + * @param {Uint8Array} state_key + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_read_state(ptr, event_type, state_key) { + const ptr0 = passArray8ToWasm0(event_type, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(state_key, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_read_state(ptr, ptr0, len0, ptr1, len1); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {bigint} sink + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_room_events(ptr, sink, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_room_events(ptr, sink, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {bigint} sink + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_to_device_events(ptr, sink, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_to_device_events(ptr, sink, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {bigint} sink + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_state_updates(ptr, sink, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_state_updates(ptr, sink, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_is_homeserver_connected(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_is_homeserver_connected(ptr, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} ptr + * @param {bigint} sink + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_connectivity(ptr, sink, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_subscribe_connectivity(ptr, sink, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} room_id + * @param {Uint8Array} event_type + * @param {Uint8Array} content_json + * @param {bigint} duration_ms + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_sticky_event(ptr, room_id, event_type, content_json, duration_ms) { + const ptr0 = passArray8ToWasm0(room_id, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(event_type, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passArray8ToWasm0(content_json, wasm.__wbindgen_malloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_sticky_event(ptr, ptr0, len0, ptr1, len1, ptr2, len2, duration_ms); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} room_id + * @param {Uint8Array} event_type + * @param {Uint8Array} state_key + * @param {Uint8Array} content_json + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_state_event(ptr, room_id, event_type, state_key, content_json) { + const ptr0 = passArray8ToWasm0(room_id, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(event_type, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passArray8ToWasm0(state_key, wasm.__wbindgen_malloc); + const len2 = WASM_VECTOR_LEN; + const ptr3 = passArray8ToWasm0(content_json, wasm.__wbindgen_malloc); + const len3 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_state_event(ptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} room_id + * @param {Uint8Array} event_type + * @param {Uint8Array} content_json + * @param {bigint} delay_ms + * @param {Uint8Array} sticky_duration_ms + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_delayed_event(ptr, room_id, event_type, content_json, delay_ms, sticky_duration_ms) { + const ptr0 = passArray8ToWasm0(room_id, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(event_type, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passArray8ToWasm0(content_json, wasm.__wbindgen_malloc); + const len2 = WASM_VECTOR_LEN; + const ptr3 = passArray8ToWasm0(sticky_duration_ms, wasm.__wbindgen_malloc); + const len3 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_delayed_event(ptr, ptr0, len0, ptr1, len1, ptr2, len2, delay_ms, ptr3, len3); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} room_id + * @param {Uint8Array} event_type + * @param {Uint8Array} state_key + * @param {Uint8Array} content_json + * @param {bigint} delay_ms + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_delayed_state_event(ptr, room_id, event_type, state_key, content_json, delay_ms) { + const ptr0 = passArray8ToWasm0(room_id, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(event_type, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passArray8ToWasm0(state_key, wasm.__wbindgen_malloc); + const len2 = WASM_VECTOR_LEN; + const ptr3 = passArray8ToWasm0(content_json, wasm.__wbindgen_malloc); + const len3 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_delayed_state_event(ptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, delay_ms); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} room_id + * @param {Uint8Array} delay_id + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_restart_delayed_event(ptr, room_id, delay_id) { + const ptr0 = passArray8ToWasm0(room_id, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(delay_id, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_restart_delayed_event(ptr, ptr0, len0, ptr1, len1); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} room_id + * @param {Uint8Array} delay_id + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_cancel_delayed_event(ptr, room_id, delay_id) { + const ptr0 = passArray8ToWasm0(room_id, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(delay_id, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_cancel_delayed_event(ptr, ptr0, len0, ptr1, len1); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} room_id + * @param {Uint8Array} slot_id + * @param {Uint8Array} member_json + * @param {Uint8Array} delay_id + * @param {Uint8Array} livekit_service_url + * @param {bigint} delay_ms + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_delegate_livekit_delayed_leave(ptr, room_id, slot_id, member_json, delay_id, livekit_service_url, delay_ms) { + const ptr0 = passArray8ToWasm0(room_id, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(slot_id, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passArray8ToWasm0(member_json, wasm.__wbindgen_malloc); + const len2 = WASM_VECTOR_LEN; + const ptr3 = passArray8ToWasm0(delay_id, wasm.__wbindgen_malloc); + const len3 = WASM_VECTOR_LEN; + const ptr4 = passArray8ToWasm0(livekit_service_url, wasm.__wbindgen_malloc); + const len4 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_delegate_livekit_delayed_leave(ptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, delay_ms); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} recipients + * @param {Uint8Array} event_type + * @param {Uint8Array} content_json + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_to_device(ptr, recipients, event_type, content_json) { + const ptr0 = passArray8ToWasm0(recipients, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(event_type, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passArray8ToWasm0(content_json, wasm.__wbindgen_malloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_send_to_device(ptr, ptr0, len0, ptr1, len1, ptr2, len2); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_get_rtc_transports(ptr) { + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_matrixdrivercallback_get_rtc_transports(ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_membershipslistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_membershipslistener(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_membershipslistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_membershipslistener(handle, f_status_.__wbg_ptr); +} + +/** + * @param {any} vtable + */ +export function ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_membershipslistener(vtable) { + wasm.ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_membershipslistener(vtable); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} memberships + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_membershipslistener_on_memberships_change(ptr, memberships, f_status_) { + const ptr0 = passArray8ToWasm0(memberships, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_membershipslistener_on_memberships_change(ptr, ptr0, len0, f_status_.__wbg_ptr); +} + +/** + * @param {any} vtable + */ +export function ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_statuslistener(vtable) { + wasm.ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_statuslistener(vtable); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} status + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_statuslistener_on_status_change(ptr, status, f_status_) { + const ptr0 = passArray8ToWasm0(status, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_statuslistener_on_status_change(ptr, ptr0, len0, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_todevicesink(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_todevicesink(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_todevicesink(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_todevicesink(handle, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} event_type + * @param {Uint8Array} sender + * @param {Uint8Array} content_json + * @param {Uint8Array} origin + * @param {Uint8Array} sender_cross_signed + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_todevicesink_emit(ptr, event_type, sender, content_json, origin, sender_cross_signed, f_status_) { + const ptr0 = passArray8ToWasm0(event_type, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(sender, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passArray8ToWasm0(content_json, wasm.__wbindgen_malloc); + const len2 = WASM_VECTOR_LEN; + const ptr3 = passArray8ToWasm0(origin, wasm.__wbindgen_malloc); + const len3 = WASM_VECTOR_LEN; + const ptr4 = passArray8ToWasm0(sender_cross_signed, wasm.__wbindgen_malloc); + const len4 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_todevicesink_emit(ptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {Uint8Array} events_json + * @param {Uint8Array} compat + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_func_compute_sessions_from_events(events_json, compat, f_status_) { + const ptr0 = passArray8ToWasm0(events_json, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(compat, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_func_compute_sessions_from_events(ptr0, len0, ptr1, len1, f_status_.__wbg_ptr); + var v3 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v3; +} + +/** + * @param {Uint8Array} impairment + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_func_impairment_severity(impairment, f_status_) { + const ptr0 = passArray8ToWasm0(impairment, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_func_impairment_severity(ptr0, len0, f_status_.__wbg_ptr); + var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v2; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_u8(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_u8(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_u8(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_u8(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_connectionslistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_connectionslistener(handle, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_roomeventsink(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_roomeventsink(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_roomeventsink(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_roomeventsink(handle, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} event_json + * @param {Uint8Array} origin + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_roomeventsink_emit(ptr, event_json, origin, f_status_) { + const ptr0 = passArray8ToWasm0(event_json, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(origin, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_roomeventsink_emit(ptr, ptr0, len0, ptr1, len1, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_stateupdatesink(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_stateupdatesink(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_stateupdatesink(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_stateupdatesink(handle, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} events_json + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_stateupdatesink_emit(ptr, events_json, f_status_) { + const ptr0 = passArray8ToWasm0(events_json, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_stateupdatesink_emit(ptr, ptr0, len0, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_statuslistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_statuslistener(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_statuslistener(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_statuslistener(handle, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_u8(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_u8(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_u8(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_u8(handle, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_i8(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_i8(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_i16(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_i16(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_i16(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_i16(handle, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_u32(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_u32(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_u32(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_u32(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_u32(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_u32(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_u32(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_u32(handle, f_status_.__wbg_ptr); + return ret >>> 0; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_i32(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_i32(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_i32(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_i32(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_i32(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_i32(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_i8(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_i8(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_i8(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_i8(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_i8(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_i8(handle, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_u16(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_u16(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_u16(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_u16(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_u16(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_u16(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_u16(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_u16(handle, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_i16(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_i16(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_i16(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_i16(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_i32(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_i32(handle, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_u64(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_u64(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_u64(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_u64(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_f32(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_f32(handle, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_f64(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_f64(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_f64(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_f64(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_f64(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_f64(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_f64(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_f64(handle, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_rust_buffer(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_rust_buffer(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_rust_buffer(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_u64(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_u64(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_u64(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_u64(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_i64(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_i64(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_i64(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_i64(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_i64(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_i64(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_i64(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_i64(handle, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_f32(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_f32(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_f32(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_f32(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_f32(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_f32(handle); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_ffi_matrix_rtc_rust_future_complete_rust_buffer(handle, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} handle + * @param {any} callback + * @param {bigint} callback_data + */ +export function ubrn_ffi_matrix_rtc_rust_future_poll_void(handle, callback, callback_data) { + wasm.ubrn_ffi_matrix_rtc_rust_future_poll_void(handle, callback, callback_data); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_cancel_void(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_cancel_void(handle); +} + +/** + * @param {bigint} handle + */ +export function ubrn_ffi_matrix_rtc_rust_future_free_void(handle) { + wasm.ubrn_ffi_matrix_rtc_rust_future_free_void(handle); +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_debug_snapshot() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_debug_snapshot(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_is_homeserver_connected() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_is_homeserver_connected(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_join() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_join(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_key_map() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_key_map(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_leave() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_leave(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_memberships() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_memberships(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_open_slot() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_open_slot(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_member_id() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_member_id(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_membership() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_membership(); + return ret; +} + +/** + * @param {any} vtable + */ +export function ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_connectionslistener(vtable) { + wasm.ubrn_uniffi_matrix_rtc_fn_init_callback_vtable_connectionslistener(vtable); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_ffi_matrix_rtc_rust_future_complete_void(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_ffi_matrix_rtc_rust_future_complete_void(handle, f_status_.__wbg_ptr); +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_func_compute_sessions_from_events() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_func_compute_sessions_from_events(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_func_impairment_severity() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_func_impairment_severity(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_connectionslistener_on_connections_change() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_connectionslistener_on_connections_change(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_connectivitysink_emit() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_connectivitysink_emit(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_close_slot() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_close_slot(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_connection_problems() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_connection_problems(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_connections() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_connections(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_transport_identity() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_own_transport_identity(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_session() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_session(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_connections_listener() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_connections_listener(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_state_event() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_state_event(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_delayed_event() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_delayed_event(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_delayed_state_event() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_delayed_state_event(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_restart_delayed_event() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_restart_delayed_event(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_cancel_delayed_event() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_cancel_delayed_event(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_delegate_livekit_delayed_leave() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_delegate_livekit_delayed_leave(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_to_device() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_to_device(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_get_rtc_transports() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_get_rtc_transports(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_get_livekit_token() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_get_livekit_token(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_key_map_listener() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_key_map_listener(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_key_rejected_listener() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_key_rejected_listener(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_memberships_listener() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_memberships_listener(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_status_listener() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_set_status_listener(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_status() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_status(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_update_application() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_ffiparticipationmanager_update_application(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_keymaplistener_on_key_map_change() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_keymaplistener_on_key_map_change(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_keyrejectedlistener_on_key_rejected() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_keyrejectedlistener_on_key_rejected(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_sticky_event() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_send_sticky_event(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_read_events() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_read_events(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_read_state() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_read_state(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_room_events() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_room_events(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_constructor_ffimatrixdriver_new() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_constructor_ffimatrixdriver_new(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_constructor_ffiparticipationmanager_new() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_constructor_ffiparticipationmanager_new(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_ffi_matrix_rtc_uniffi_contract_version() { + const ret = wasm.ubrn_ffi_matrix_rtc_uniffi_contract_version(); + return ret >>> 0; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_to_device_events() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_to_device_events(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_state_updates() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_state_updates(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_is_homeserver_connected() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_is_homeserver_connected(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_connectivity() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_matrixdrivercallback_subscribe_connectivity(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_membershipslistener_on_memberships_change() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_membershipslistener_on_memberships_change(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_roomeventsink_emit() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_roomeventsink_emit(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_stateupdatesink_emit() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_stateupdatesink_emit(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_statuslistener_on_status_change() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_statuslistener_on_status_change(); + return ret; +} + +/** + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_checksum_method_todevicesink_emit() { + const ret = wasm.ubrn_uniffi_matrix_rtc_checksum_method_todevicesink_emit(); + return ret; +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} connections + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_method_connectionslistener_on_connections_change(ptr, connections, f_status_) { + const ptr0 = passArray8ToWasm0(connections, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_method_connectionslistener_on_connections_change(ptr, ptr0, len0, f_status_.__wbg_ptr); +} + +/** + * @param {Uint8Array} room_id + * @param {Uint8Array} slot_id + * @param {Uint8Array} user_id + * @param {Uint8Array} device_id + * @param {bigint} driver + * @param {Uint8Array} config + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_constructor_ffiparticipationmanager_new(room_id, slot_id, user_id, device_id, driver, config, f_status_) { + const ptr0 = passArray8ToWasm0(room_id, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(slot_id, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passArray8ToWasm0(user_id, wasm.__wbindgen_malloc); + const len2 = WASM_VECTOR_LEN; + const ptr3 = passArray8ToWasm0(device_id, wasm.__wbindgen_malloc); + const len3 = WASM_VECTOR_LEN; + const ptr4 = passArray8ToWasm0(config, wasm.__wbindgen_malloc); + const len4 = WASM_VECTOR_LEN; + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_constructor_ffiparticipationmanager_new(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, driver, ptr4, len4, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_close_slot(ptr) { + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_close_slot(ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_connection_problems(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_connection_problems(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_connections(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_connections(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_debug_snapshot(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_debug_snapshot(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_is_homeserver_connected(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_is_homeserver_connected(ptr, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} intent + * @param {Uint8Array} params + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_join(ptr, intent, params) { + const ptr0 = passArray8ToWasm0(intent, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(params, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_join(ptr, ptr0, len0, ptr1, len1); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} ptr + * @param {RustCallStatus} f_status_ + * @returns {Uint8Array} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_key_map(ptr, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_key_map(ptr, f_status_.__wbg_ptr); + var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + return v1; +} + +/** + * @param {bigint} ptr + * @param {Uint8Array} code + * @param {Uint8Array} reason + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_leave(ptr, code, reason) { + const ptr0 = passArray8ToWasm0(code, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passArray8ToWasm0(reason, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_ffiparticipationmanager_leave(ptr, ptr0, len0, ptr1, len1); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_connectivitysink(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_connectivitysink(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_connectivitysink(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_connectivitysink(handle, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} ptr + * @param {number} connected + * @param {RustCallStatus} f_status_ + * @returns {number} + */ +export function ubrn_uniffi_matrix_rtc_fn_method_connectivitysink_emit(ptr, connected, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_method_connectivitysink_emit(ptr, connected, f_status_.__wbg_ptr); + return ret; +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_ffimatrixdriver(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_ffimatrixdriver(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_ffimatrixdriver(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_ffimatrixdriver(handle, f_status_.__wbg_ptr); +} + +/** + * @param {bigint} callback + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_constructor_ffimatrixdriver_new(callback, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_constructor_ffimatrixdriver_new(callback, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + * @returns {bigint} + */ +export function ubrn_uniffi_matrix_rtc_fn_clone_ffiparticipationmanager(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + const ret = wasm.ubrn_uniffi_matrix_rtc_fn_clone_ffiparticipationmanager(handle, f_status_.__wbg_ptr); + return BigInt.asUintN(64, ret); +} + +/** + * @param {bigint} handle + * @param {RustCallStatus} f_status_ + */ +export function ubrn_uniffi_matrix_rtc_fn_free_ffiparticipationmanager(handle, f_status_) { + _assertClass(f_status_, RustCallStatus); + wasm.ubrn_uniffi_matrix_rtc_fn_free_ffiparticipationmanager(handle, f_status_.__wbg_ptr); +} + +function __wbg_adapter_24(arg0, arg1) { + wasm._dyn_core_e48f7a02345547b9___ops__function__FnMut_____Output______as_wasm_bindgen_65e498d0bf4959ae___closure__WasmClosure___describe__invoke______(arg0, arg1); +} + +function __wbg_adapter_27(arg0, arg1, arg2) { + wasm.closure531_externref_shim(arg0, arg1, arg2); +} + +const ForeignFutureCompleteF32Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompletef32_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteF32 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteF32Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompletef32_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteF32} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteF32); + wasm.foreignfuturecompletef32_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteF64Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompletef64_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteF64 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteF64Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompletef64_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteF64} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteF64); + wasm.foreignfuturecompletef64_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteI16Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompletei16_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteI16 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteI16Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompletei16_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteI16} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteI16); + wasm.foreignfuturecompletei16_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteI32Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompletei32_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteI32 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteI32Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompletei32_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteI32} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteI32); + wasm.foreignfuturecompletei32_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteI64Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompletei64_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteI64 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteI64Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompletei64_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteI64} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteI64); + wasm.foreignfuturecompletei64_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteI8Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompletei8_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteI8 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteI8Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompletei8_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteI8} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteI8); + wasm.foreignfuturecompletei8_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteRustBufferFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompleterustbuffer_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteRustBuffer { + + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(ForeignFutureCompleteRustBuffer.prototype); + obj.__wbg_ptr = ptr; + ForeignFutureCompleteRustBufferFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteRustBufferFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompleterustbuffer_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteRustBuffer} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteRustBuffer); + wasm.foreignfuturecompleterustbuffer_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteU16Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompleteu16_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteU16 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteU16Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompleteu16_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteU16} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteU16); + wasm.foreignfuturecompleteu16_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteU32Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompleteu32_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteU32 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteU32Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompleteu32_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteU32} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteU32); + wasm.foreignfuturecompleteu32_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteU64Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompleteu64_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteU64 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteU64Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompleteu64_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteU64} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteU64); + wasm.foreignfuturecompleteu64_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteU8Finalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompleteu8_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteU8 { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteU8Finalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompleteu8_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteU8} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteU8); + wasm.foreignfuturecompleteu8_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const ForeignFutureCompleteVoidFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_foreignfuturecompletevoid_free(ptr >>> 0, 1)); + +export class ForeignFutureCompleteVoid { + + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(ForeignFutureCompleteVoid.prototype); + obj.__wbg_ptr = ptr; + ForeignFutureCompleteVoidFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ForeignFutureCompleteVoidFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_foreignfuturecompletevoid_free(ptr, 0); + } + /** + * @param {ForeignFutureCompleteVoid} _ctx + * @param {bigint} callback_data + * @param {any} result + */ + call(_ctx, callback_data, result) { + _assertClass(_ctx, ForeignFutureCompleteVoid); + wasm.foreignfuturecompletevoid_call(this.__wbg_ptr, _ctx.__wbg_ptr, callback_data, result); + } +} + +const RustCallStatusFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_rustcallstatus_free(ptr >>> 0, 1)); + +export class RustCallStatus { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + RustCallStatusFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_rustcallstatus_free(ptr, 0); + } + /** + * @returns {number} + */ + get code() { + const ret = wasm.__wbg_get_rustcallstatus_code(this.__wbg_ptr); + return ret; + } + /** + * @param {number} arg0 + */ + set code(arg0) { + wasm.__wbg_set_rustcallstatus_code(this.__wbg_ptr, arg0); + } + /** + * @param {Uint8Array | null} [bytes] + */ + set errorBuf(bytes) { + var ptr0 = isLikeNone(bytes) ? 0 : passArray8ToWasm0(bytes, wasm.__wbindgen_malloc); + var len0 = WASM_VECTOR_LEN; + wasm.rustcallstatus_set_error_buf(this.__wbg_ptr, ptr0, len0); + } + constructor() { + const ret = wasm.rustcallstatus_new(); + this.__wbg_ptr = ret >>> 0; + RustCallStatusFinalization.register(this, this.__wbg_ptr, this); + return this; + } + /** + * @returns {Uint8Array | undefined} + */ + get errorBuf() { + const ptr = this.__destroy_into_raw(); + const ret = wasm.rustcallstatus_error_buf(ptr); + let v1; + if (ret[0] !== 0) { + v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); + } + return v1; + } +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + + } catch (e) { + if (module.headers.get('Content-Type') != 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + + } else { + return instance; + } + } +} + +function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbg_buffer_609cc3eee51ed158 = function(arg0) { + const ret = arg0.buffer; + return ret; + }; + imports.wbg.__wbg_call_0056921d632def66 = function(arg0, arg1, arg2) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2)); + return ret; + }; + imports.wbg.__wbg_call_00ed0f3262ca10fc = function(arg0, arg1, arg2) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2)); + return ret; + }; + imports.wbg.__wbg_call_02529374d31ad97c = function(arg0, arg1, arg2) { + arg0.call(arg1, BigInt.asUintN(64, arg2)); + }; + imports.wbg.__wbg_call_0433755a93443d6e = function(arg0, arg1, arg2, arg3) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), BigInt.asUintN(64, arg3)); + return ret; + }; + imports.wbg.__wbg_call_05dc34bdb8662702 = function(arg0, arg1, arg2, arg3) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), BigInt.asUintN(64, arg3)); + return ret; + }; + imports.wbg.__wbg_call_0ba4077697ab1405 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + var v2 = getArrayU8FromWasm0(arg7, arg8).slice(); + wasm.__wbindgen_free(arg7, arg8 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, v2, ForeignFutureCompleteRustBuffer.__wrap(arg9), BigInt.asUintN(64, arg10)); + return ret; + }; + imports.wbg.__wbg_call_21960420ca81bc05 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + var v2 = getArrayU8FromWasm0(arg7, arg8).slice(); + wasm.__wbindgen_free(arg7, arg8 * 1, 1); + var v3 = getArrayU8FromWasm0(arg9, arg10).slice(); + wasm.__wbindgen_free(arg9, arg10 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, v2, v3, ForeignFutureCompleteRustBuffer.__wrap(arg11), BigInt.asUintN(64, arg12)); + return ret; + }; + imports.wbg.__wbg_call_2798409ff618ef7d = function(arg0, arg1, arg2) { + arg0.call(arg1, BigInt.asUintN(64, arg2)); + }; + imports.wbg.__wbg_call_2f24f710f0dd8d10 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1); + return ret; + }; + imports.wbg.__wbg_call_4022622bc30aaf84 = function(arg0, arg1, arg2) { + arg0.call(arg1, BigInt.asUintN(64, arg2)); + }; + imports.wbg.__wbg_call_45ca295cb4469524 = function(arg0, arg1, arg2, arg3) { + arg0.call(arg1, BigInt.asUintN(64, arg2), arg3); + }; + imports.wbg.__wbg_call_45f35e4128d6c004 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, ForeignFutureCompleteRustBuffer.__wrap(arg5), BigInt.asUintN(64, arg6)); + return ret; + }; + imports.wbg.__wbg_call_4614c0a573de577f = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + var v2 = getArrayU8FromWasm0(arg7, arg8).slice(); + wasm.__wbindgen_free(arg7, arg8 * 1, 1); + var v3 = getArrayU8FromWasm0(arg9, arg10).slice(); + wasm.__wbindgen_free(arg9, arg10 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, v2, v3, BigInt.asUintN(64, arg11), ForeignFutureCompleteRustBuffer.__wrap(arg12), BigInt.asUintN(64, arg13)); + return ret; + }; + imports.wbg.__wbg_call_496b92421c092b65 = function(arg0, arg1, arg2) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2)); + return ret; + }; + imports.wbg.__wbg_call_571d4611879307e7 = function(arg0, arg1, arg2, arg3) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), BigInt.asUintN(64, arg3)); + return ret; + }; + imports.wbg.__wbg_call_65221d24e12159bf = function(arg0, arg1, arg2, arg3, arg4) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), ForeignFutureCompleteRustBuffer.__wrap(arg3), BigInt.asUintN(64, arg4)); + return ret; + }; + imports.wbg.__wbg_call_65360e4d1b0f41fa = function(arg0, arg1, arg2) { + arg0.call(arg1, BigInt.asUintN(64, arg2)); + }; + imports.wbg.__wbg_call_672a4d21634d4a24 = function() { return handleError(function (arg0, arg1) { + const ret = arg0.call(arg1); + return ret; + }, arguments) }; + imports.wbg.__wbg_call_67bcb4b184601516 = function(arg0, arg1, arg2) { + arg0.call(arg1, BigInt.asUintN(64, arg2)); + }; + imports.wbg.__wbg_call_79e16370d6861b4f = function(arg0, arg1, arg2, arg3, arg4) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0); + return ret; + }; + imports.wbg.__wbg_call_7cccdd69e0791ae2 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.call(arg1, arg2); + return ret; + }, arguments) }; + imports.wbg.__wbg_call_85dc743acd81f8e1 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + var v2 = getArrayU8FromWasm0(arg7, arg8).slice(); + wasm.__wbindgen_free(arg7, arg8 * 1, 1); + var v3 = getArrayU8FromWasm0(arg9, arg10).slice(); + wasm.__wbindgen_free(arg9, arg10 * 1, 1); + var v4 = getArrayU8FromWasm0(arg11, arg12).slice(); + wasm.__wbindgen_free(arg11, arg12 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, v2, v3, v4, BigInt.asUintN(64, arg13), ForeignFutureCompleteVoid.__wrap(arg14), BigInt.asUintN(64, arg15)); + return ret; + }; + imports.wbg.__wbg_call_9a894f10d06a53c9 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, ForeignFutureCompleteVoid.__wrap(arg7), BigInt.asUintN(64, arg8)); + return ret; + }; + imports.wbg.__wbg_call_9f7a841426436a44 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, ForeignFutureCompleteVoid.__wrap(arg7), BigInt.asUintN(64, arg8)); + return ret; + }; + imports.wbg.__wbg_call_a6145cafbb6c59f0 = function(arg0, arg1, arg2) { + arg0.call(arg1, BigInt.asUintN(64, arg2)); + }; + imports.wbg.__wbg_call_a79f116fab0f1a0a = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1); + return ret; + }; + imports.wbg.__wbg_call_abeb9928262c69b8 = function(arg0, arg1, arg2, arg3, arg4) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0); + return ret; + }; + imports.wbg.__wbg_call_b66d526ecf55c81d = function(arg0, arg1, arg2) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2)); + return ret; + }; + imports.wbg.__wbg_call_bd883beab9425b7c = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, arg7 >>> 0, ForeignFutureCompleteRustBuffer.__wrap(arg8), BigInt.asUintN(64, arg9)); + return ret; + }; + imports.wbg.__wbg_call_c9c4e766b640b52d = function(arg0, arg1, arg2, arg3) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), BigInt.asUintN(64, arg3)); + return ret; + }; + imports.wbg.__wbg_call_ca06027b14b17766 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + var v2 = getArrayU8FromWasm0(arg7, arg8).slice(); + wasm.__wbindgen_free(arg7, arg8 * 1, 1); + var v3 = getArrayU8FromWasm0(arg10, arg11).slice(); + wasm.__wbindgen_free(arg10, arg11 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, v2, BigInt.asUintN(64, arg9), v3, ForeignFutureCompleteRustBuffer.__wrap(arg12), BigInt.asUintN(64, arg13)); + return ret; + }; + imports.wbg.__wbg_call_db19d6a7a08fbd37 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + var v2 = getArrayU8FromWasm0(arg7, arg8).slice(); + wasm.__wbindgen_free(arg7, arg8 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, v2, BigInt.asUintN(64, arg9), ForeignFutureCompleteRustBuffer.__wrap(arg10), BigInt.asUintN(64, arg11)); + return ret; + }; + imports.wbg.__wbg_call_dd8fc90cc1eed1a0 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + var v1 = getArrayU8FromWasm0(arg5, arg6).slice(); + wasm.__wbindgen_free(arg5, arg6 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0, v1, ForeignFutureCompleteRustBuffer.__wrap(arg7), BigInt.asUintN(64, arg8)); + return ret; + }; + imports.wbg.__wbg_call_df53672c5883e9e7 = function(arg0, arg1, arg2, arg3, arg4) { + var v0 = getArrayU8FromWasm0(arg3, arg4).slice(); + wasm.__wbindgen_free(arg3, arg4 * 1, 1); + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2), v0); + return ret; + }; + imports.wbg.__wbg_call_f95ee156aae25631 = function(arg0, arg1, arg2) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2)); + return ret; + }; + imports.wbg.__wbg_call_fb7f89f8d567b160 = function(arg0, arg1, arg2) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2)); + return ret; + }; + imports.wbg.__wbg_call_fedc2bfb0eb6df90 = function(arg0, arg1, arg2) { + arg0.call(arg1, BigInt.asUintN(64, arg2)); + }; + imports.wbg.__wbg_call_ff7e7c3b656ecebf = function(arg0, arg1, arg2) { + const ret = arg0.call(arg1, BigInt.asUintN(64, arg2)); + return ret; + }; + imports.wbg.__wbg_callstatus_ab813cdd9fb59b5c = function(arg0) { + const ret = arg0.call_status; + _assertClass(ret, RustCallStatus); + var ptr1 = ret.__destroy_into_raw(); + return ptr1; + }; + imports.wbg.__wbg_canceldelayedevent_f1442079ae4bb697 = function(arg0) { + const ret = arg0.cancel_delayed_event; + return ret; + }; + imports.wbg.__wbg_clearTimeout_5a54f8841c30079a = function(arg0) { + const ret = clearTimeout(arg0); + return ret; + }; + imports.wbg.__wbg_code_a1790f546af56cdc = function(arg0) { + const ret = arg0.code; + return ret; + }; + imports.wbg.__wbg_code_b18c52258a7d4327 = function(arg0) { + const ret = arg0.code; + return ret; + }; + imports.wbg.__wbg_crypto_86f2631e91b51511 = function(arg0) { + const ret = arg0.crypto; + return ret; + }; + imports.wbg.__wbg_delegatelivekitdelayedleave_991fce363b53064f = function(arg0) { + const ret = arg0.delegate_livekit_delayed_leave; + return ret; + }; + imports.wbg.__wbg_errorbuf_59fafcd45c02f900 = function(arg0, arg1) { + const ret = arg1.errorBuf; + var ptr1 = isLikeNone(ret) ? 0 : passArray8ToWasm0(ret, wasm.__wbindgen_malloc); + var len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_errorbuf_9e8687be915296d2 = function(arg0, arg1) { + const ret = arg1.errorBuf; + var ptr1 = isLikeNone(ret) ? 0 : passArray8ToWasm0(ret, wasm.__wbindgen_malloc); + var len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_free_b85a6024660bd07c = function(arg0) { + const ret = arg0.free; + return ret; + }; + imports.wbg.__wbg_getRandomValues_b3f15fcbfabb0f8b = function() { return handleError(function (arg0, arg1) { + arg0.getRandomValues(arg1); + }, arguments) }; + imports.wbg.__wbg_getlivekittoken_269ca0b4c594770a = function(arg0) { + const ret = arg0.get_livekit_token; + return ret; + }; + imports.wbg.__wbg_getrtctransports_8be200fadbe143dc = function(arg0) { + const ret = arg0.get_rtc_transports; + return ret; + }; + imports.wbg.__wbg_handle_c2870606e589531b = function(arg0) { + const ret = arg0.handle; + return ret; + }; + imports.wbg.__wbg_ishomeserverconnected_dc5057f7242a591e = function(arg0) { + const ret = arg0.is_homeserver_connected; + return ret; + }; + imports.wbg.__wbg_msCrypto_d562bbe83e0d4b91 = function(arg0) { + const ret = arg0.msCrypto; + return ret; + }; + imports.wbg.__wbg_new_a12002a7f91c75be = function(arg0) { + const ret = new Uint8Array(arg0); + return ret; + }; + imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function(arg0, arg1) { + const ret = new Function(getStringFromWasm0(arg0, arg1)); + return ret; + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_d97e637ebe145a9a = function(arg0, arg1, arg2) { + const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0); + return ret; + }; + imports.wbg.__wbg_newwithlength_a381634e90c276d4 = function(arg0) { + const ret = new Uint8Array(arg0 >>> 0); + return ret; + }; + imports.wbg.__wbg_node_e1f24f89a7336c2e = function(arg0) { + const ret = arg0.node; + return ret; + }; + imports.wbg.__wbg_now_807e54c39636c349 = function() { + const ret = Date.now(); + return ret; + }; + imports.wbg.__wbg_onconnectionschange_cb540732b536bde5 = function(arg0) { + const ret = arg0.on_connections_change; + return ret; + }; + imports.wbg.__wbg_onkeymapchange_7af99be63015699f = function(arg0) { + const ret = arg0.on_key_map_change; + return ret; + }; + imports.wbg.__wbg_onkeyrejected_0811c486ecda07ea = function(arg0) { + const ret = arg0.on_key_rejected; + return ret; + }; + imports.wbg.__wbg_onmembershipschange_72a01e47629908a2 = function(arg0) { + const ret = arg0.on_memberships_change; + return ret; + }; + imports.wbg.__wbg_onstatuschange_926e83644f91f32d = function(arg0) { + const ret = arg0.on_status_change; + return ret; + }; + imports.wbg.__wbg_pointee_26637032edb8983c = function(arg0) { + const ret = arg0.pointee; + return isLikeNone(ret) ? 0xFFFFFF : ret; + }; + imports.wbg.__wbg_process_3975fd6c72f520aa = function(arg0) { + const ret = arg0.process; + return ret; + }; + imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function(arg0) { + queueMicrotask(arg0); + }; + imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function(arg0) { + const ret = arg0.queueMicrotask; + return ret; + }; + imports.wbg.__wbg_randomFillSync_f8c153b79f285817 = function() { return handleError(function (arg0, arg1) { + arg0.randomFillSync(arg1); + }, arguments) }; + imports.wbg.__wbg_readevents_98385cdfe47bdeee = function(arg0) { + const ret = arg0.read_events; + return ret; + }; + imports.wbg.__wbg_readstate_a52da7153b66ed2b = function(arg0) { + const ret = arg0.read_state; + return ret; + }; + imports.wbg.__wbg_require_b74f47fc2d022fd6 = function() { return handleError(function () { + const ret = module.require; + return ret; + }, arguments) }; + imports.wbg.__wbg_resolve_4851785c9c5f573d = function(arg0) { + const ret = Promise.resolve(arg0); + return ret; + }; + imports.wbg.__wbg_restartdelayedevent_0d0c2a4a4627841f = function(arg0) { + const ret = arg0.restart_delayed_event; + return ret; + }; + imports.wbg.__wbg_returnvalue_03774948f0205329 = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_returnvalue_21195623004109c3 = function(arg0, arg1) { + const ret = arg1.return_value; + const ptr1 = passArray8ToWasm0(ret, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_returnvalue_2620087eeddb8b84 = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_returnvalue_38e0584d82a3ca2e = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_returnvalue_3e9d3c06adc527d8 = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_returnvalue_6e396db732507afb = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_returnvalue_7debfc20c2457c0f = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_returnvalue_92b4f6af41751b5a = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_returnvalue_a10e08181b6e3347 = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_returnvalue_b2583433f7d5bc58 = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_returnvalue_fe6442da8edb6e26 = function(arg0) { + const ret = arg0.return_value; + return ret; + }; + imports.wbg.__wbg_senddelayedevent_1a17e9af52ac70ce = function(arg0) { + const ret = arg0.send_delayed_event; + return ret; + }; + imports.wbg.__wbg_senddelayedstateevent_7310e92bccc3dd9a = function(arg0) { + const ret = arg0.send_delayed_state_event; + return ret; + }; + imports.wbg.__wbg_sendstateevent_b46311c2c989f673 = function(arg0) { + const ret = arg0.send_state_event; + return ret; + }; + imports.wbg.__wbg_sendstickyevent_ae6f44183a0c7825 = function(arg0) { + const ret = arg0.send_sticky_event; + return ret; + }; + imports.wbg.__wbg_sendtodevice_a3e33fb31230ba64 = function(arg0) { + const ret = arg0.send_to_device; + return ret; + }; + imports.wbg.__wbg_setTimeout_db2dbaeefb6f39c7 = function() { return handleError(function (arg0, arg1) { + const ret = setTimeout(arg0, arg1); + return ret; + }, arguments) }; + imports.wbg.__wbg_set_65595bdd868b3009 = function(arg0, arg1, arg2) { + arg0.set(arg1, arg2 >>> 0); + }; + imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() { + const ret = typeof global === 'undefined' ? null : global; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() { + const ret = typeof globalThis === 'undefined' ? null : globalThis; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() { + const ret = typeof self === 'undefined' ? null : self; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() { + const ret = typeof window === 'undefined' ? null : window; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_subarray_aa9065fa9dc5df96 = function(arg0, arg1, arg2) { + const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0); + return ret; + }; + imports.wbg.__wbg_subscribeconnectivity_a75e094330a03e3e = function(arg0) { + const ret = arg0.subscribe_connectivity; + return ret; + }; + imports.wbg.__wbg_subscriberoomevents_2e4d5ca6372fe4cd = function(arg0) { + const ret = arg0.subscribe_room_events; + return ret; + }; + imports.wbg.__wbg_subscribestateupdates_06939632cde2db00 = function(arg0) { + const ret = arg0.subscribe_state_updates; + return ret; + }; + imports.wbg.__wbg_subscribetodeviceevents_afcc4eb08f3d890c = function(arg0) { + const ret = arg0.subscribe_to_device_events; + return ret; + }; + imports.wbg.__wbg_then_44b73946d2fb3e7d = function(arg0, arg1) { + const ret = arg0.then(arg1); + return ret; + }; + imports.wbg.__wbg_unifficlone_25a64f9a81fb5b28 = function(arg0) { + const ret = arg0.uniffi_clone; + return ret; + }; + imports.wbg.__wbg_unifficlone_6471a1309ee8fc26 = function(arg0) { + const ret = arg0.uniffi_clone; + return ret; + }; + imports.wbg.__wbg_unifficlone_95a28cf009142cf6 = function(arg0) { + const ret = arg0.uniffi_clone; + return ret; + }; + imports.wbg.__wbg_unifficlone_9de5c992227b473d = function(arg0) { + const ret = arg0.uniffi_clone; + return ret; + }; + imports.wbg.__wbg_unifficlone_f4e9653dc2b93c0c = function(arg0) { + const ret = arg0.uniffi_clone; + return ret; + }; + imports.wbg.__wbg_unifficlone_fe0240768db0e3d6 = function(arg0) { + const ret = arg0.uniffi_clone; + return ret; + }; + imports.wbg.__wbg_uniffifree_00c9e670fc140d79 = function(arg0) { + const ret = arg0.uniffi_free; + return ret; + }; + imports.wbg.__wbg_uniffifree_2b26526e84ac127f = function(arg0) { + const ret = arg0.uniffi_free; + return ret; + }; + imports.wbg.__wbg_uniffifree_313a3d469f2be86d = function(arg0) { + const ret = arg0.uniffi_free; + return ret; + }; + imports.wbg.__wbg_uniffifree_7f877a9bb1d1f761 = function(arg0) { + const ret = arg0.uniffi_free; + return ret; + }; + imports.wbg.__wbg_uniffifree_af6c094a1e08186f = function(arg0) { + const ret = arg0.uniffi_free; + return ret; + }; + imports.wbg.__wbg_uniffifree_c315e1fbfd7f9e6a = function(arg0) { + const ret = arg0.uniffi_free; + return ret; + }; + imports.wbg.__wbg_versions_4e31226f5e8dc909 = function(arg0) { + const ret = arg0.versions; + return ret; + }; + imports.wbg.__wbindgen_cb_drop = function(arg0) { + const obj = arg0.original; + if (obj.cnt-- == 1) { + obj.a = 0; + return true; + } + const ret = false; + return ret; + }; + imports.wbg.__wbindgen_closure_wrapper2151 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 520, __wbg_adapter_24); + return ret; + }; + imports.wbg.__wbindgen_closure_wrapper2175 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 532, __wbg_adapter_27); + return ret; + }; + imports.wbg.__wbindgen_init_externref_table = function() { + const table = wasm.__wbindgen_export_3; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + ; + }; + imports.wbg.__wbindgen_is_function = function(arg0) { + const ret = typeof(arg0) === 'function'; + return ret; + }; + imports.wbg.__wbindgen_is_object = function(arg0) { + const val = arg0; + const ret = typeof(val) === 'object' && val !== null; + return ret; + }; + imports.wbg.__wbindgen_is_string = function(arg0) { + const ret = typeof(arg0) === 'string'; + return ret; + }; + imports.wbg.__wbindgen_is_undefined = function(arg0) { + const ret = arg0 === undefined; + return ret; + }; + imports.wbg.__wbindgen_memory = function() { + const ret = wasm.memory; + return ret; + }; + imports.wbg.__wbindgen_string_new = function(arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return ret; + }; + imports.wbg.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + + return imports; +} + +function __wbg_init_memory(imports, memory) { + +} + +function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedDataViewMemory0 = null; + cachedUint8ArrayMemory0 = null; + + + wasm.__wbindgen_start(); + return wasm; +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + + if (typeof module !== 'undefined') { + if (Object.getPrototypeOf(module) === Object.prototype) { + ({module} = module) + } else { + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') + } + } + + const imports = __wbg_get_imports(); + + __wbg_init_memory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(module_or_path) { + if (wasm !== undefined) return wasm; + + + if (typeof module_or_path !== 'undefined') { + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { + ({module_or_path} = module_or_path) + } else { + console.warn('using deprecated parameters for the initialization function; pass a single object instead') + } + } + + + const imports = __wbg_get_imports(); + + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { + module_or_path = fetch(module_or_path); + } + + __wbg_init_memory(imports); + + const { instance, module } = await __wbg_load(await module_or_path, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync }; +export default __wbg_init; diff --git a/src/matrix-rtc-sdk/generated/wasm-bindgen/index_bg.wasm b/src/matrix-rtc-sdk/generated/wasm-bindgen/index_bg.wasm new file mode 100644 index 000000000..26f57cbad Binary files /dev/null and b/src/matrix-rtc-sdk/generated/wasm-bindgen/index_bg.wasm differ diff --git a/src/matrix-rtc-sdk/index.test.ts b/src/matrix-rtc-sdk/index.test.ts new file mode 100644 index 000000000..d22bd7215 --- /dev/null +++ b/src/matrix-rtc-sdk/index.test.ts @@ -0,0 +1,136 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { beforeAll, describe, expect, it } from "vitest"; + +import { initMatrixRtcSdkForTests } from "../utils/test-matrix-rtc"; +import { + FfiElementCallCompat, + FfiMatrixDriver, + FfiParticipationManager, + FfiStatus, + computeSessionsFromEvents, + type FfiLivekitToken, + type FfiLivekitTokenRequest, + type FfiRtcTransport, + type FfiSendEventResponse, + type FfiToDeviceDelivery, + type FfiToDeviceRecipient, + type MatrixDriverCallback, +} from "."; + +const ROOM_ID = "!room:example.org"; + +describe("matrix-rtc-sdk", () => { + beforeAll(async () => { + await initMatrixRtcSdkForTests(); + }); + + it("computes a session from raw events without a manager", () => { + const join = JSON.stringify({ + type: "m.rtc.member", + sender: "@alice:example.org", + event_id: "$1", + room_id: ROOM_ID, + origin_server_ts: Date.now(), + msc4354_sticky: { duration_ms: 240_000 }, + content: { + slot_id: "m.call#ROOM", + msc4354_sticky_key: "m-1", + member: { id: "m-1", membership: "join" }, + application: { type: "m.call" }, + transports: { + published: [ + { type: "livekit", livekit_service_url: "https://lk.example.org" }, + ], + can_subscribe: ["livekit"], + }, + }, + }); + const [session] = computeSessionsFromEvents( + [join], + FfiElementCallCompat.StickyEvents, + ); + expect(session.memberCount).toBe(1); + expect(session.members[0].eventId).toBe("$1"); + expect(session.isActive).toBe(true); + }); + + it("constructs a manager over a TypeScript driver and starts disconnected", () => { + const driver = new FfiMatrixDriver(new InertDriver()); + const manager = new FfiParticipationManager( + ROOM_ID, + "m.call#ROOM", + "@me:example.org", + "MYDEV", + driver, + { + compat: FfiElementCallCompat.StickyEvents, + manageMediaKeys: false, + requireCrossSignedSender: false, + useKeyDelayMs: 1000n, + }, + ); + expect(FfiStatus.Disconnected.instanceOf(manager.status())).toBe(true); + expect(manager.memberships()).toEqual([]); + expect(manager.ownTransportIdentity()).toBeUndefined(); + manager.uniffiDestroy(); + }); +}); + +/** A driver that answers every read with nothing and never sends. */ +class InertDriver implements MatrixDriverCallback { + public async sendStickyEvent(): Promise { + return Promise.resolve({ eventId: "$sticky", delayId: undefined }); + } + public async sendStateEvent(): Promise { + return Promise.resolve({ eventId: "$state", delayId: undefined }); + } + public async sendDelayedEvent(): Promise { + return Promise.resolve("delay"); + } + public async sendDelayedStateEvent(): Promise { + return Promise.resolve("delay"); + } + public async restartDelayedEvent(): Promise { + return Promise.resolve(); + } + public async cancelDelayedEvent(): Promise { + return Promise.resolve(); + } + public async delegateLivekitDelayedLeave(): Promise { + return Promise.resolve(); + } + public async sendToDevice( + recipients: FfiToDeviceRecipient[], + ): Promise { + return Promise.resolve( + recipients.map((recipient) => ({ recipient, error: undefined })), + ); + } + public async getRtcTransports(): Promise { + return Promise.resolve([]); + } + public async getLivekitToken( + request: FfiLivekitTokenRequest, + ): Promise { + return Promise.resolve({ jwt: "jwt", url: request.url }); + } + public async readEvents(): Promise { + return Promise.resolve([]); + } + public async readState(): Promise { + return Promise.resolve([]); + } + public subscribeRoomEvents(): void {} + public subscribeToDeviceEvents(): void {} + public subscribeStateUpdates(): void {} + public subscribeConnectivity(): void {} + public isHomeserverConnected(): boolean { + return true; + } +} diff --git a/src/matrix-rtc-sdk/index.ts b/src/matrix-rtc-sdk/index.ts new file mode 100644 index 000000000..6db8b385d --- /dev/null +++ b/src/matrix-rtc-sdk/index.ts @@ -0,0 +1,90 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +/** + * The MatrixRTC SDK: the Rust `matrix-rtc` crate through its uniffi web + * bindings. Everything Element Call needs to *participate* in a MatrixRTC + * session — the session projection, our own membership with its keep-alive, + * transport tokens and the media key exchange — lives in the crate; this + * module loads it and re-exports the surface Element Call uses. + * + * The bindings under `generated/` are vendored by + * `scripts/sync-matrix-rtc-sdk.sh` and never edited by hand. + */ + +import initAsync, { type InitInput } from "./generated/wasm-bindgen/index.js"; +import bindings from "./generated/matrix_rtc"; + +export { + FfiMatrixDriver, + FfiParticipationManager, + FfiElementCallCompat, + FfiEventOrigin, + FfiStatus, + FfiDisconnectCause, + FfiJoinError, + FfiKeepAlive, + FfiMembershipState, + FfiTransportIntent, + RtcError, + computeSessionsFromEvents, +} from "./generated/matrix_rtc"; +export type { + FfiConnectionData, + FfiConnectionWithMembers, + FfiJoinParams, + FfiLivekitToken, + FfiLivekitTokenRequest, + FfiMediaKey, + FfiMember, + FfiMembership, + FfiParticipationConfig, + FfiRtcTransport, + FfiSendEventResponse, + FfiSessionSnapshot, + FfiToDeviceDelivery, + FfiToDeviceRecipient, + ConnectivitySinkLike, + MatrixDriverCallback, + RoomEventSinkLike, + StateUpdateSinkLike, + ToDeviceSinkLike, +} from "./generated/matrix_rtc"; + +/** + * Where to load the wasm from. A URL (or anything `fetch` accepts) in a + * browser; bytes or a compiled module where there is nothing to fetch from, + * such as tests. + */ +export type MatrixRtcWasmSource = InitInput; + +let loading: Promise | null = null; + +/** + * Loads and initialises the SDK. Idempotent: the first call decides the + * source, later calls await the same load. + * + * Without a `source`, the wasm is the one bundled next to this module (an + * asset URL in the app builds). A host that serves the file from somewhere + * else, or a test runner with no server, passes its own. + */ +export async function initMatrixRtcSdk( + source?: MatrixRtcWasmSource, +): Promise { + loading ??= (async (): Promise => { + await initAsync({ module_or_path: source ?? (await bundledWasm()) }); + bindings.initialize(); + })(); + await loading; +} + +/** The wasm as the bundler placed it, resolved lazily so nothing is fetched until needed. */ +async function bundledWasm(): Promise { + const { default: url } = + await import("./generated/wasm-bindgen/index_bg.wasm?url"); + return url; +} diff --git a/src/state/rtc/CallParticipation.test.ts b/src/state/rtc/CallParticipation.test.ts new file mode 100644 index 000000000..e768f39f0 --- /dev/null +++ b/src/state/rtc/CallParticipation.test.ts @@ -0,0 +1,393 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { beforeAll, describe, expect, it } from "vitest"; + +import { MatrixRTCMode } from "../../config/ConfigOptions"; +import { + MOCK_LK_SERVICE_URL, + MockRtcMatrixDriver, + roomEncryptionEvent, + slotEvent, + waitFor, +} from "../../driver/MockRtcMatrixDriver"; +import { + FfiDisconnectCause, + FfiElementCallCompat, + FfiStatus, + type FfiMediaKey, +} from "../../matrix-rtc-sdk"; +import { initMatrixRtcSdkForTests } from "../../utils/test-matrix-rtc"; +import { testScope } from "../../utils/test"; +import { ObservableScope } from "../ObservableScope"; +import { CallParticipation } from "./CallParticipation"; +import { errorForStatus } from "./errors"; +import { + compatForMode, + joinParamsFromConfig, + participationConfig, +} from "./joinParams"; +import { publishOnLivekit, receiveOnly } from "./transportIntent"; +import { + MatrixRTCTransportMissingError, + NoOpenSlotError, + StickyEventsRequiredError, +} from "../../utils/errors"; + +const session = { + delayed_leave: { delay_ms: 15_000 }, + delegated_delayed_leave: { delay_ms: 3_600_000 }, + network_error_retry_ms: 1000, + wait_for_key_rotation_ms: 50, +}; + +const joinParams = joinParamsFromConfig({ + session, + delegateDelayedLeave: false, +}); + +function create( + driver: MockRtcMatrixDriver, + overrides: { manageMediaKeys?: boolean; transportFallbackUrl?: string } = {}, + scope = testScope(), +): CallParticipation { + return new CallParticipation( + scope, + driver, + driver.roomId, + driver.userId, + driver.deviceId, + { + config: participationConfig({ + mode: MatrixRTCMode.Matrix_2_0, + manageMediaKeys: overrides.manageMediaKeys ?? true, + session, + }), + transportFallbackUrl: overrides.transportFallbackUrl, + }, + ); +} + +/** A room where nobody has started a call yet, and we may. */ +const openSlot = { encrypted: false, canOpen: true }; + +const peer = { + userId: "@peer:example.org", + deviceId: "PEERDEV", + memberId: "m-peer", +}; + +describe("CallParticipation", () => { + beforeAll(async () => { + await initMatrixRtcSdkForTests(); + }); + + it("starts disconnected and follows a remote member in and out", async () => { + // somebody already started the call: the slot is open + const driver = new MockRtcMatrixDriver({ + roomState: [slotEvent({ status: "open" })], + }); + const callParticipation = create(driver); + expect( + FfiStatus.Disconnected.instanceOf(callParticipation.status$.value), + ).toBe(true); + expect(callParticipation.memberships$.value.value).toEqual([]); + + driver.peerJoins(peer); + // getters are fresh at once; the listener fires a tick later + await waitFor( + "peer listed", + () => callParticipation.memberships$.value.value.length === 1, + ); + const [membership] = callParticipation.memberships$.value.value; + expect(membership.member.memberId).toBe(peer.memberId); + expect(membership.connections).toEqual([MOCK_LK_SERVICE_URL]); + expect(callParticipation.session$.value.memberCount).toBe(1); + + driver.peerLeaves(peer); + await waitFor( + "peer gone", + () => callParticipation.memberships$.value.value.length === 0, + ); + }); + + it("joins publishing, exposes our identity before the echo and our membership after it", async () => { + const driver = new MockRtcMatrixDriver(); + const callParticipation = create(driver); + await callParticipation.join(publishOnLivekit(), joinParams, openSlot); + + expect( + FfiStatus.Connected.instanceOf(callParticipation.status$.value), + ).toBe(true); + const ownMemberId = callParticipation.ownMemberId$.value; + expect(ownMemberId).toBeTruthy(); + expect(callParticipation.ownTransportIdentity$.value).toBeTruthy(); + // nobody had started a call: we opened the slot before joining + expect(callParticipation.session$.value.slotOpen).toBe(true); + const [slot] = driver.calls("stateEvent"); + expect(slot.eventType).toBe("org.matrix.msc4143.rtc.slot"); + expect(slot.stateKey).toBe("m.call#ROOM"); + expect(slot.content).toEqual({ + status: "open", + application: { type: "m.call" }, + }); + + await waitFor( + "own echo", + () => callParticipation.ownMembership$.value !== null, + ); + expect(callParticipation.ownMembership$.value?.member.memberId).toBe( + ownMemberId, + ); + expect(callParticipation.ownMembership$.value?.transportIdentity).toBe( + callParticipation.ownTransportIdentity$.value, + ); + expect(callParticipation.connections$.value).toHaveLength(1); + expect(callParticipation.connections$.value[0].connection.serviceUrl).toBe( + MOCK_LK_SERVICE_URL, + ); + // discovery: the bare intent asked the homeserver + expect(driver.calls("getRtcTransports")).toHaveLength(1); + expect(driver.calls("getLivekitToken")[0].slotId).toBe("m.call#ROOM"); + + await callParticipation.leave(); + const status = callParticipation.status$.value; + expect(FfiStatus.Disconnected.instanceOf(status)).toBe(true); + if (FfiStatus.Disconnected.instanceOf(status)) + expect(FfiDisconnectCause.LeftByHost.instanceOf(status.inner.cause)).toBe( + true, + ); + // ...and again: a manager can be reused, with a fresh member id + await callParticipation.join(publishOnLivekit(), joinParams, openSlot); + expect(callParticipation.ownMemberId$.value).not.toBe(ownMemberId); + await callParticipation.leave(); + }); + + it("does not open a slot that is already open, and refuses to join without the power to open one", async () => { + const open = new MockRtcMatrixDriver({ + roomState: [slotEvent({ status: "open" })], + }); + const p1 = create(open); + await p1.join(receiveOnly(), joinParams, { + encrypted: false, + canOpen: false, + }); + expect(open.calls("stateEvent")).toEqual([]); + await p1.leave(); + + const closed = new MockRtcMatrixDriver(); + const p2 = create(closed); + await expect( + p2.join(receiveOnly(), joinParams, { encrypted: false, canOpen: false }), + ).rejects.toBeInstanceOf(NoOpenSlotError); + expect(closed.calls("stateEvent")).toEqual([]); + expect(closed.calls("stickyEvent")).toEqual([]); + expect(FfiStatus.Disconnected.instanceOf(p2.status$.value)).toBe(true); + + // an encrypted room gets a slot that prescribes per-member keys + const encryptedRoom = new MockRtcMatrixDriver({ + roomState: [roomEncryptionEvent()], + }); + const p3 = create(encryptedRoom); + await p3.join(receiveOnly(), joinParams, { + encrypted: true, + canOpen: true, + }); + expect(encryptedRoom.calls("stateEvent")[0].content).toEqual({ + status: "open", + application: { type: "m.call" }, + encryption: { type: "m.per_member" }, + }); + expect(p3.session$.value.encrypted).toBe(true); + await p3.leave(); + }); + + it("falls back to the configured transport when the homeserver has none or fails", async () => { + const none = new MockRtcMatrixDriver({ transports: [] }); + const p1 = create(none, { transportFallbackUrl: "https://lk.config" }); + await p1.join(publishOnLivekit(), joinParams, openSlot); + expect(none.calls("getLivekitToken")[0].url).toBe("https://lk.config"); + await p1.leave(); + + const failing = new MockRtcMatrixDriver(); + failing.failTransportDiscovery = true; + const p2 = create(failing, { transportFallbackUrl: "https://lk.config" }); + await p2.join(publishOnLivekit(), joinParams, openSlot); + expect(failing.calls("getLivekitToken")[0].url).toBe("https://lk.config"); + await p2.leave(); + + // a custom URL in the intent skips discovery altogether + const custom = new MockRtcMatrixDriver(); + const p3 = create(custom); + await p3.join(publishOnLivekit("https://lk.custom"), joinParams, openSlot); + expect(custom.calls("getRtcTransports")).toHaveLength(0); + expect(custom.calls("getLivekitToken")[0].url).toBe("https://lk.custom"); + await p3.leave(); + }); + + it("streams key changes and filters members that left holding our key", async () => { + const driver = new MockRtcMatrixDriver({ + roomState: [ + roomEncryptionEvent(), + slotEvent({ status: "open", encrypted: true }), + ], + }); + const callParticipation = create(driver); + const changes: FfiMediaKey[] = []; + callParticipation.keyChanges$.subscribe((k) => changes.push(k)); + driver.addPeer(peer); + await callParticipation.join(receiveOnly(), joinParams, openSlot); + driver.peerJoins(peer); + await waitFor("peer key", () => + callParticipation.keyMap$.value.some((k) => k.memberId === peer.memberId), + ); + expect(changes.some((k) => k.memberId === peer.memberId)).toBe(true); + driver.peerLeaves(peer); + // the crate keeps a LeftWithKeys entry; the behavior does not + await waitFor( + "peer gone from memberships", + () => + !callParticipation.memberships$.value.value.some( + (m) => m.member.memberId === peer.memberId, + ), + ); + await callParticipation.leave(); + }); + + it("does not exchange keys when the call manages none", async () => { + const driver = new MockRtcMatrixDriver(); + const callParticipation = create(driver, { manageMediaKeys: false }); + driver.addPeer(peer); + await callParticipation.join(receiveOnly(), joinParams, openSlot); + driver.peerJoins(peer); + await new Promise((r) => setTimeout(r, 50)); + expect(driver.calls("toDevice")).toEqual([]); + await callParticipation.leave(); + }); + + it("surfaces a lost homeserver as a critical impairment in the status", async () => { + const driver = new MockRtcMatrixDriver(); + const callParticipation = create(driver); + await callParticipation.join(receiveOnly(), joinParams, openSlot); + driver.setHomeserverConnected(false); + await waitFor("impairment in status$", () => { + const status = callParticipation.status$.value; + return ( + FfiStatus.Connected.instanceOf(status) && + status.inner.impairments[0]?.tag === "HomeserverUnreachable" + ); + }); + driver.setHomeserverConnected(true); + await waitFor("impairment cleared", () => { + const status = callParticipation.status$.value; + return ( + FfiStatus.Connected.instanceOf(status) && + status.inner.impairments.length === 0 + ); + }); + await callParticipation.leave(); + }); + + it("leaves and destroys the manager when the scope ends", async () => { + const driver = new MockRtcMatrixDriver(); + const scope = new ObservableScope(); + const callParticipation = create(driver, {}, scope); + await callParticipation.join(receiveOnly(), joinParams, openSlot); + scope.end(); + await waitFor( + "leave sent", + () => + driver + .calls("stickyEvent") + .some( + (c) => + c.content.member === undefined || + c.content.msc4354_sticky_key !== undefined, + ) && driver.calls("cancelDelayed").length === 1, + ); + expect(callParticipation.debugSnapshot()).toBe("{}"); + await expect( + callParticipation.join(receiveOnly(), joinParams, openSlot), + ).rejects.toThrow("ended"); + }); + + it("turns terminal causes into Element Call errors", async () => { + const driver = new MockRtcMatrixDriver({ transports: [] }); + const callParticipation = create(driver); + await expect( + callParticipation.join(publishOnLivekit(), joinParams, openSlot), + ).rejects.toThrow(); + expect( + errorForStatus(callParticipation.status$.value, { + domain: "example.org", + stickyEventsSupported: true, + }), + ).toBeInstanceOf(MatrixRTCTransportMissingError); + + const noSticky = new MockRtcMatrixDriver(); + noSticky.refuseStickyEvents = true; + const p2 = create(noSticky); + await expect( + p2.join(receiveOnly(), joinParams, openSlot), + ).rejects.toThrow(); + expect( + errorForStatus(p2.status$.value, { + domain: "example.org", + stickyEventsSupported: false, + }), + ).toBeInstanceOf(StickyEventsRequiredError); + + const fine = create(new MockRtcMatrixDriver()); + expect( + errorForStatus(fine.status$.value, { + domain: "example.org", + stickyEventsSupported: true, + }), + ).toBeNull(); + }); + + it("derives the crate's parameters from Element Call's config", () => { + expect(compatForMode(MatrixRTCMode.Compatibility)).toBe( + FfiElementCallCompat.StateEvents, + ); + expect(compatForMode(MatrixRTCMode.Matrix_2_0)).toBe( + FfiElementCallCompat.StickyEvents, + ); + expect(joinParams).toEqual({ + applicationType: "m.call", + intent: undefined, + // js-sdk's 4 h default, capped at the sticky hour + stickyDurationMs: 3_600_000n, + keepAliveTimeoutMs: 15_000n, + degradedLifetimeMs: undefined, + delegateDelayedLeave: false, + }); + expect( + joinParamsFromConfig({ + session: { ...session, membership_event_expiry_ms: 60_000 }, + callIntent: "video", + delegateDelayedLeave: true, + }), + ).toMatchObject({ + intent: "video", + stickyDurationMs: 60_000n, + delegateDelayedLeave: true, + }); + expect( + participationConfig({ + mode: MatrixRTCMode.Compatibility, + manageMediaKeys: false, + session, + }), + ).toEqual({ + compat: FfiElementCallCompat.StateEvents, + manageMediaKeys: false, + requireCrossSignedSender: false, + useKeyDelayMs: 50n, + }); + }); +}); diff --git a/src/state/rtc/CallParticipation.ts b/src/state/rtc/CallParticipation.ts new file mode 100644 index 000000000..fd9638618 --- /dev/null +++ b/src/state/rtc/CallParticipation.ts @@ -0,0 +1,494 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger"; +import { + BehaviorSubject, + type Observable, + Subject, + combineLatest, + map, +} from "rxjs"; + +import { type RtcMatrixDriver } from "../../driver/RtcMatrixDriver"; +import { + FfiMatrixDriver, + FfiMembershipState, + FfiParticipationManager, + FfiStatus, + type FfiConnectionWithMembers, + type FfiJoinParams, + type FfiLivekitToken, + type FfiLivekitTokenRequest, + type FfiMediaKey, + type FfiMembership, + type FfiParticipationConfig, + type FfiRtcTransport, + type FfiSendEventResponse, + type FfiSessionSnapshot, + type FfiToDeviceDelivery, + type FfiToDeviceRecipient, + type FfiTransportIntent, + type ConnectivitySinkLike, + type RoomEventSinkLike, + type StateUpdateSinkLike, + type ToDeviceSinkLike, +} from "../../matrix-rtc-sdk"; +import { type Behavior } from "../Behavior"; +import { Epoch, type ObservableScope, trackEpoch } from "../ObservableScope"; +import { NoOpenSlotError } from "../../utils/errors"; +import { ELEMENT_CALL_APPLICATION, ELEMENT_CALL_SLOT_ID } from "./slot"; +import { LIVEKIT_TRANSPORT_TYPE } from "./transportIntent"; + +/** + * What to do about the room's slot when joining. A call needs an open + * MatrixRTC slot (`m.rtc.slot` state); a room that never had a call has + * none, and the client that starts the call opens it — if its user has the + * power level to send the state event. + */ +export interface SlotPolicy { + /** Whether the slot, if we open it, prescribes per-member media encryption (an encrypted room). */ + encrypted: boolean; + /** Whether this user may send the slot state event (`RoomInfo.canOpenSlot`). */ + canOpen: boolean; +} + +/** How long to wait for the seed, and for our own slot event to echo back. */ +const SLOT_WAIT_MS = 15_000; + +export interface CallParticipationOptions { + /** One manager per `(room, slot)`; Element Call has one slot per room. */ + slotId?: string; + config: FfiParticipationConfig; + /** + * A LiveKit service URL to fall back on when the homeserver advertises no + * transport (or cannot be asked) — Element Call's `config.json` value, + * which is Element Call's business rather than the host's. + */ + transportFallbackUrl?: string; + logger?: Logger; +} + +/** + * Element Call's view of one participation in a MatrixRTC session: the + * crate's `FfiParticipationManager` as behaviors. "Participation" is the + * crate's word for the FFI side; this is the RxJS wrapper a call is built on. + * + * The crate does everything Matrix: it projects the session from the + * driver's events, publishes and keeps alive our own membership, mints + * transport tokens and exchanges media keys. This class owns the manager + * for the scope's lifetime, seeds each behavior from the manager's getter and + * keeps it current from the manager's listener, and ends the participation + * (leaving if still joined) when the scope ends. + */ +export class CallParticipation { + private readonly logger: Logger; + private readonly matrixDriver: FfiMatrixDriver; + private readonly manager: FfiParticipationManager; + private ended = false; + + private readonly membershipsSubject$: BehaviorSubject; + private readonly connectionsSubject$: BehaviorSubject< + FfiConnectionWithMembers[] + >; + private readonly keyMapSubject$: BehaviorSubject; + private readonly keyChangesSubject$ = new Subject(); + private readonly statusSubject$: BehaviorSubject; + private readonly sessionSubject$: BehaviorSubject; + private readonly ownMemberIdSubject$: BehaviorSubject; + private readonly ownTransportIdentitySubject$: BehaviorSubject; + + /** + * One entry per joined member, ourselves included once our own membership + * has echoed back from the homeserver. Members that left but may still hold + * our media key (`LeftWithKeys`) are not listed. + */ + public readonly memberships$: Behavior>; + /** The LiveKit rooms to hold, with the token for each. */ + public readonly connections$: Behavior; + /** Every media key in use, ours and theirs, one per (member, index). */ + public readonly keyMap$: Behavior; + /** The single key that changed, as it changes. */ + public readonly keyChanges$: Observable = + this.keyChangesSubject$; + public readonly status$: Behavior; + /** Slot open?, encrypted?, member count, seed honesty. */ + public readonly session$: Behavior; + /** Our member id, from the moment `join()` starts; null while not joined. */ + public readonly ownMemberId$: Behavior; + /** Our LiveKit participant identity, known as early as the member id. */ + public readonly ownTransportIdentity$: Behavior; + /** Our own entry in `memberships$`, once echoed. */ + public readonly ownMembership$: Behavior; + + public constructor( + scope: ObservableScope, + driver: RtcMatrixDriver, + roomId: string, + userId: string, + deviceId: string, + options: CallParticipationOptions, + ) { + this.logger = (options.logger ?? rootLogger).getChild( + "[CallParticipation]", + ); + const rtcDriver = + options.transportFallbackUrl === undefined + ? driver + : new TransportFallbackDriver( + driver, + options.transportFallbackUrl, + this.logger, + ); + this.matrixDriver = new FfiMatrixDriver(rtcDriver); + this.manager = new FfiParticipationManager( + roomId, + options.slotId ?? ELEMENT_CALL_SLOT_ID, + userId, + deviceId, + this.matrixDriver, + options.config, + ); + + this.membershipsSubject$ = new BehaviorSubject( + joinedOnly(this.manager.memberships()), + ); + this.connectionsSubject$ = new BehaviorSubject(this.manager.connections()); + this.keyMapSubject$ = new BehaviorSubject(this.manager.keyMap()); + this.statusSubject$ = new BehaviorSubject(this.manager.status()); + this.sessionSubject$ = new BehaviorSubject(this.manager.session()); + this.ownMemberIdSubject$ = new BehaviorSubject( + this.manager.ownMemberId() ?? null, + ); + this.ownTransportIdentitySubject$ = new BehaviorSubject( + this.manager.ownTransportIdentity() ?? null, + ); + + this.manager.setMembershipsListener({ + onMembershipsChange: (memberships) => { + if (this.ended) return; + this.membershipsSubject$.next(joinedOnly(memberships)); + this.sessionSubject$.next(this.manager.session()); + this.refreshOwnIdentity(); + }, + }); + this.manager.setConnectionsListener({ + onConnectionsChange: (connections) => { + if (!this.ended) this.connectionsSubject$.next(connections); + }, + }); + this.manager.setKeyMapListener({ + onKeyMapChange: (keyMap, change) => { + if (this.ended) return; + this.keyMapSubject$.next(keyMap); + this.keyChangesSubject$.next(change); + }, + }); + this.manager.setStatusListener({ + onStatusChange: (status) => { + if (this.ended) return; + this.statusSubject$.next(status); + this.sessionSubject$.next(this.manager.session()); + this.refreshOwnIdentity(); + }, + }); + this.manager.setKeyRejectedListener({ + onKeyRejected: (memberId, reason) => + this.logger.warn(`Discarded a media key from ${memberId}: ${reason}`), + }); + + this.memberships$ = scope.behavior( + this.membershipsSubject$.pipe(trackEpoch()), + new Epoch(this.membershipsSubject$.value), + ); + this.connections$ = scope.behavior(this.connectionsSubject$); + this.keyMap$ = scope.behavior(this.keyMapSubject$); + this.status$ = scope.behavior(this.statusSubject$); + this.session$ = scope.behavior(this.sessionSubject$); + this.ownMemberId$ = scope.behavior(this.ownMemberIdSubject$); + this.ownTransportIdentity$ = scope.behavior( + this.ownTransportIdentitySubject$, + ); + this.ownMembership$ = scope.behavior( + combineLatest([this.memberships$, this.ownMemberId$]).pipe( + map( + ([memberships, ownMemberId]) => + memberships.value.find((m) => m.member.memberId === ownMemberId) ?? + null, + ), + ), + ); + + scope.onEnd(() => void this.end()); + } + + /** + * Join the session, opening the room's slot first when nobody has + * (`slot`). Resolves once our membership is published, or rejects: with + * {@link NoOpenSlotError} when there is no slot and we may not open one, + * otherwise with the crate's typed error. The status keeps reporting from + * there. + */ + public async join( + intent: FfiTransportIntent, + params: FfiJoinParams, + slot: SlotPolicy, + ): Promise { + if (this.ended) throw new Error("The participation has ended"); + try { + await this.ensureOpenSlot(slot); + await this.manager.join(intent, params); + } finally { + this.refreshAll(); + } + } + + private async ensureOpenSlot(slot: SlotPolicy): Promise { + // The crate's own `join` waits for the seed too, but the slot check has + // to come first: a slot read that has not finished looks like no slot. + await this.waitUntil( + () => this.manager.session().seeded, + "the session seed", + ); + if (this.manager.session().slotOpen === true) return; + if (!slot.canOpen) throw new NoOpenSlotError(); + this.logger.info( + `No open slot in the room; opening ${ELEMENT_CALL_SLOT_ID} (encrypted: ${slot.encrypted})`, + ); + await this.manager.openSlot(ELEMENT_CALL_APPLICATION, slot.encrypted); + // Open only once the homeserver has echoed the state back. + await this.waitUntil( + () => this.manager.session().slotOpen === true, + "our slot event to echo back", + ); + } + + private async waitUntil( + condition: () => boolean, + what: string, + ): Promise { + const deadline = Date.now() + SLOT_WAIT_MS; + while (!condition()) { + if (this.ended) throw new Error("The participation has ended"); + if (Date.now() > deadline) + throw new Error(`Timed out waiting for ${what}`); + await new Promise((resolve) => setTimeout(resolve, 20)); + } + } + + /** Leave the session. A no-op when not joined. */ + public async leave(code?: string, reason?: string): Promise { + if (this.ended) return; + if (FfiStatus.Disconnected.instanceOf(this.manager.status())) return; + try { + await this.manager.leave(code, reason); + } finally { + this.refreshAll(); + } + } + + /** The crate's diagnostics dump, for rageshakes. Not a UI contract. */ + public debugSnapshot(): string { + return this.ended ? "{}" : this.manager.debugSnapshot(); + } + + private refreshOwnIdentity(): void { + this.ownMemberIdSubject$.next(this.manager.ownMemberId() ?? null); + this.ownTransportIdentitySubject$.next( + this.manager.ownTransportIdentity() ?? null, + ); + } + + /** After a join or leave the getters are fresh before any listener fires. */ + private refreshAll(): void { + if (this.ended) return; + this.statusSubject$.next(this.manager.status()); + this.membershipsSubject$.next(joinedOnly(this.manager.memberships())); + this.connectionsSubject$.next(this.manager.connections()); + this.sessionSubject$.next(this.manager.session()); + this.refreshOwnIdentity(); + } + + private async end(): Promise { + if (this.ended) return; + this.ended = true; + if (!FfiStatus.Disconnected.instanceOf(this.manager.status())) { + try { + await this.manager.leave(undefined, undefined); + } catch (e) { + this.logger.warn("Could not leave the session cleanly", e); + } + } + this.keyChangesSubject$.complete(); + this.manager.uniffiDestroy(); + this.matrixDriver.uniffiDestroy(); + } +} + +function joinedOnly(memberships: FfiMembership[]): FfiMembership[] { + return memberships.filter((m) => m.state === FfiMembershipState.Joined); +} + +/** + * Answers transport discovery with a configured LiveKit service URL when the + * host's homeserver advertises none or cannot be asked — the precedence + * Element Call has always had (homeserver first, `config.json` second) — + * and delegates everything else to the host's driver untouched. + */ +class TransportFallbackDriver implements RtcMatrixDriver { + public constructor( + private readonly inner: RtcMatrixDriver, + private readonly fallbackUrl: string, + private readonly logger: Logger, + ) {} + + public async getRtcTransports(): Promise { + let transports: FfiRtcTransport[] = []; + try { + transports = await this.inner.getRtcTransports(); + } catch (e) { + this.logger.info( + "Transport discovery failed; falling back to the configured LiveKit service", + e, + ); + } + if (transports.some((t) => t.transportType === LIVEKIT_TRANSPORT_TYPE)) + return transports; + this.logger.info( + "The homeserver advertises no LiveKit transport; using the configured one", + ); + return [ + { + transportType: LIVEKIT_TRANSPORT_TYPE, + propertiesJson: JSON.stringify({ + livekit_service_url: this.fallbackUrl, + }), + }, + ]; + } + + public async sendStickyEvent( + roomId: string, + eventType: string, + contentJson: string, + durationMs: bigint, + ): Promise { + return this.inner.sendStickyEvent( + roomId, + eventType, + contentJson, + durationMs, + ); + } + public async sendStateEvent( + roomId: string, + eventType: string, + stateKey: string, + contentJson: string, + ): Promise { + return this.inner.sendStateEvent(roomId, eventType, stateKey, contentJson); + } + public async sendDelayedEvent( + roomId: string, + eventType: string, + contentJson: string, + delayMs: bigint, + stickyDurationMs: bigint | undefined, + ): Promise { + return this.inner.sendDelayedEvent( + roomId, + eventType, + contentJson, + delayMs, + stickyDurationMs, + ); + } + public async sendDelayedStateEvent( + roomId: string, + eventType: string, + stateKey: string, + contentJson: string, + delayMs: bigint, + ): Promise { + return this.inner.sendDelayedStateEvent( + roomId, + eventType, + stateKey, + contentJson, + delayMs, + ); + } + public async restartDelayedEvent( + roomId: string, + delayId: string, + ): Promise { + return this.inner.restartDelayedEvent(roomId, delayId); + } + public async cancelDelayedEvent( + roomId: string, + delayId: string, + ): Promise { + return this.inner.cancelDelayedEvent(roomId, delayId); + } + public async delegateLivekitDelayedLeave( + roomId: string, + slotId: string, + memberJson: string, + delayId: string, + livekitServiceUrl: string | undefined, + delayMs: bigint, + ): Promise { + return this.inner.delegateLivekitDelayedLeave( + roomId, + slotId, + memberJson, + delayId, + livekitServiceUrl, + delayMs, + ); + } + public async sendToDevice( + recipients: FfiToDeviceRecipient[], + eventType: string, + contentJson: string, + ): Promise { + return this.inner.sendToDevice(recipients, eventType, contentJson); + } + public async getLivekitToken( + request: FfiLivekitTokenRequest, + ): Promise { + return this.inner.getLivekitToken(request); + } + public async readEvents( + eventType: string, + stateKey: string | undefined, + limit: number, + ): Promise { + return this.inner.readEvents(eventType, stateKey, limit); + } + public async readState( + eventType: string, + stateKey: string | undefined, + ): Promise { + return this.inner.readState(eventType, stateKey); + } + public subscribeRoomEvents(sink: RoomEventSinkLike): void { + this.inner.subscribeRoomEvents(sink); + } + public subscribeToDeviceEvents(sink: ToDeviceSinkLike): void { + this.inner.subscribeToDeviceEvents(sink); + } + public subscribeStateUpdates(sink: StateUpdateSinkLike): void { + this.inner.subscribeStateUpdates(sink); + } + public subscribeConnectivity(sink: ConnectivitySinkLike): void { + this.inner.subscribeConnectivity(sink); + } + public isHomeserverConnected(): boolean { + return this.inner.isHomeserverConnected(); + } +} diff --git a/src/state/rtc/errors.ts b/src/state/rtc/errors.ts new file mode 100644 index 000000000..e28dd3320 --- /dev/null +++ b/src/state/rtc/errors.ts @@ -0,0 +1,67 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { + FfiDisconnectCause, + FfiJoinError, + FfiStatus, +} from "../../matrix-rtc-sdk"; +import { + ConnectionLostError, + type ElementCallError, + MatrixRTCTransportMissingError, + MembershipManagerError, + StickyEventsRequiredError, +} from "../../utils/errors"; + +export interface DisconnectContext { + /** The homeserver's domain, for the "no transport" message. */ + domain: string; + /** Whether the homeserver was found to accept sticky events. */ + stickyEventsSupported: boolean; +} + +/** + * The error a participation ended with, or null when it ended on purpose + * (never joined, or the host left). + * + * The crate splits failures into recoverable state (`impairments`, which the + * call UI shows as interruptions) and terminal causes; only the latter are + * errors here. + */ +export function errorForStatus( + status: FfiStatus, + context: DisconnectContext, +): ElementCallError | null { + if (!FfiStatus.Disconnected.instanceOf(status)) return null; + const cause = status.inner.cause; + if ( + FfiDisconnectCause.NeverJoined.instanceOf(cause) || + FfiDisconnectCause.LeftByHost.instanceOf(cause) + ) + return null; + if (FfiDisconnectCause.JoinFailed.instanceOf(cause)) { + const error = cause.inner.error; + if (FfiJoinError.NoTransport.instanceOf(error)) + return new MatrixRTCTransportMissingError(context.domain); + // A homeserver that refuses sticky events fails the very first send. + if (FfiJoinError.Driver.instanceOf(error) && !context.stickyEventsSupported) + return new StickyEventsRequiredError(); + return new MembershipManagerError(new Error(describeJoinError(error))); + } + // SlotClosed, ManagerStopped: we are out and nothing will bring us back. + return new ConnectionLostError(); +} + +function describeJoinError(error: FfiJoinError): string { + const inner: unknown = "inner" in error ? error.inner : undefined; + const message = + typeof inner === "object" && inner !== null && "message" in inner + ? inner.message + : undefined; + return typeof message === "string" ? `${error.tag}: ${message}` : error.tag; +} diff --git a/src/state/rtc/joinParams.ts b/src/state/rtc/joinParams.ts new file mode 100644 index 000000000..0ea3271bc --- /dev/null +++ b/src/state/rtc/joinParams.ts @@ -0,0 +1,104 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { + type ResolvedConfigOptions, + MatrixRTCMode, +} from "../../config/ConfigOptions"; +import { + FfiElementCallCompat, + type FfiJoinParams, + type FfiParticipationConfig, +} from "../../matrix-rtc-sdk"; +import { ELEMENT_CALL_APPLICATION } from "./slot"; + +/** matrix-js-sdk caps a sticky membership at an hour; so does the crate. */ +const MAX_STICKY_DURATION_MS = 60 * 60 * 1000; +/** matrix-js-sdk's default membership expiry when the config names none. */ +const DEFAULT_MEMBERSHIP_EXPIRY_MS = 4 * 60 * 60 * 1000; + +/** + * Which pre-2026 Element Call dialect the crate speaks for a given mode. + * `compatibility` is MSC3401 state events; `matrix_2_0` is the sticky + * dialect deployed Element Call clients read today. Spec MSC4143 (`Off`) + * waits until Element Call opens slots. + */ +export function compatForMode(mode: MatrixRTCMode): FfiElementCallCompat { + switch (mode) { + case MatrixRTCMode.Compatibility: + return FfiElementCallCompat.StateEvents; + case MatrixRTCMode.Matrix_2_0: + return FfiElementCallCompat.StickyEvents; + } +} + +export interface ParticipationConfigInputs { + mode: MatrixRTCMode; + /** Whether this call encrypts media with per-participant keys. */ + manageMediaKeys: boolean; + /** + * Whether to discard media keys from senders not reported as cross-signed + * (MSC4153). Off for parity with matrix-js-sdk, which never checked, and + * because a passwordless guest cannot be cross-signed. + * + * TODO: we want this on. Turning it on needs (1) the SPA's passwordless + * users to be cross-signed or given another way in, and (2) the crate to + * report the sender's verdict on the tile while the check is off + * (`FfiMediaKeyState.senderCrossSigned`, plan item C10), so the UI can + * show an unverified sender before the switch flips. + */ + requireCrossSignedSender?: boolean; + session: ResolvedConfigOptions["matrix_rtc_session"]; +} + +export function participationConfig({ + mode, + manageMediaKeys, + requireCrossSignedSender = false, + session, +}: ParticipationConfigInputs): FfiParticipationConfig { + return { + compat: compatForMode(mode), + manageMediaKeys, + requireCrossSignedSender, + useKeyDelayMs: BigInt(session.wait_for_key_rotation_ms ?? 1000), + }; +} + +export interface JoinParamsInputs { + session: ResolvedConfigOptions["matrix_rtc_session"]; + /** `m.call.intent`: what kind of call the user is starting. */ + callIntent?: string; + /** Hand the delayed leave to the SFU (MSC4195) — only where the probe said it can. */ + delegateDelayedLeave: boolean; +} + +/** + * The crate's join parameters from Element Call's `matrix_rtc_session` + * config. Keys the crate has no knob for (`restart_ms`, `restart_timeout_ms`, + * `network_error_retry_ms`, `key_rotation_participant_limit`, + * `delegated_delayed_leave.*`) have no effect any more. + */ +export function joinParamsFromConfig({ + session, + callIntent, + delegateDelayedLeave, +}: JoinParamsInputs): FfiJoinParams { + return { + applicationType: ELEMENT_CALL_APPLICATION, + intent: callIntent, + stickyDurationMs: BigInt( + Math.min( + session.membership_event_expiry_ms ?? DEFAULT_MEMBERSHIP_EXPIRY_MS, + MAX_STICKY_DURATION_MS, + ), + ), + keepAliveTimeoutMs: BigInt(session.delayed_leave.delay_ms), + degradedLifetimeMs: undefined, + delegateDelayedLeave, + }; +} diff --git a/src/state/rtc/slot.ts b/src/state/rtc/slot.ts new file mode 100644 index 000000000..137737c1a --- /dev/null +++ b/src/state/rtc/slot.ts @@ -0,0 +1,19 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +/** + * The MatrixRTC slot Element Call lives in, `{application}#{id}` per + * MSC4143. One per room: the application is a call and the id is the room's. + */ +export const ELEMENT_CALL_SLOT_ID = "m.call#ROOM"; +export const ELEMENT_CALL_APPLICATION = "m.call"; +/** + * The state event type the crate opens a slot with (the unstable spelling + * deployed homeservers know). Opening a slot needs the power level to send + * it, which is what `RoomInfo.canOpenSlot` answers. + */ +export const ELEMENT_CALL_SLOT_EVENT_TYPE = "org.matrix.msc4143.rtc.slot"; diff --git a/src/state/rtc/transportIntent.ts b/src/state/rtc/transportIntent.ts new file mode 100644 index 000000000..dac9a75c7 --- /dev/null +++ b/src/state/rtc/transportIntent.ts @@ -0,0 +1,34 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { FfiTransportIntent } from "../../matrix-rtc-sdk"; + +export const LIVEKIT_TRANSPORT_TYPE = "livekit"; + +/** + * Publish on LiveKit. With a `serviceUrl` (a developer's custom URL) that + * transport is used as given; without one the crate discovers it through the + * driver's `getRtcTransports`, which is where the homeserver's answer and + * Element Call's config fallback come in. + */ +export function publishOnLivekit(serviceUrl?: string): FfiTransportIntent { + return new FfiTransportIntent.Publish({ + transport: { + transportType: LIVEKIT_TRANSPORT_TYPE, + propertiesJson: JSON.stringify( + serviceUrl === undefined ? {} : { livekit_service_url: serviceUrl }, + ), + }, + }); +} + +/** Take part without publishing media: recorders, observers, tests. */ +export function receiveOnly(): FfiTransportIntent { + return new FfiTransportIntent.ReceiveOnly({ + canSubscribe: [LIVEKIT_TRANSPORT_TYPE], + }); +} diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 73b904c95..40a69a633 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -21,6 +21,8 @@ export enum ErrorCode { INSUFFICIENT_CAPACITY_ERROR = "INSUFFICIENT_CAPACITY_ERROR", E2EE_NOT_SUPPORTED = "E2EE_NOT_SUPPORTED", STICKY_EVENTS_NOT_SUPPORTED = "STICKY_EVENTS_NOT_SUPPORTED", + /** The room has no open MatrixRTC slot and the user may not open one. */ + NO_OPEN_SLOT = "NO_OPEN_SLOT", OPEN_ID_ERROR = "OPEN_ID_ERROR", NO_MATRIX_2_AUTHORIZATION_SERVICE = "NO_MATRIX_2_0_AUTHORIZATION_SERVICE", SFU_ERROR = "SFU_ERROR", @@ -140,6 +142,20 @@ export class StickyEventsRequiredError extends ElementCallError { ); } } +/** + * A call needs an open MatrixRTC slot in the room. Nobody has opened one, and + * this user lacks the power level to send the slot state event. + */ +export class NoOpenSlotError extends ElementCallError { + public constructor() { + super( + i18n.t("error.no_open_slot"), + ErrorCode.NO_OPEN_SLOT, + ErrorCategory.CONFIGURATION_ISSUE, + i18n.t("error.no_open_slot_description"), + ); + } +} /** * Error indicating that end-to-end encryption is not supported in the current environment. diff --git a/src/utils/test-matrix-rtc.ts b/src/utils/test-matrix-rtc.ts new file mode 100644 index 000000000..09e2ed4bd --- /dev/null +++ b/src/utils/test-matrix-rtc.ts @@ -0,0 +1,31 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +import { initMatrixRtcSdk } from "../matrix-rtc-sdk"; + +/** + * Loads the MatrixRTC SDK for a unit test. The browser builds fetch the wasm + * by URL; under vitest there is no server, so the bytes come from disk. + * + * Only suites that construct a participation manager need this; do not put + * it in the global setup, where every test file would pay for the boot. + */ +export async function initMatrixRtcSdkForTests(): Promise { + // Relative to this file: under the jsdom environment `import.meta.url` is + // not a file URL and `process.cwd()` is not the repository, but vitest + // still provides `__dirname`. + const wasm = readFileSync( + resolve( + __dirname, + "../matrix-rtc-sdk/generated/wasm-bindgen/index_bg.wasm", + ), + ); + await initMatrixRtcSdk(wasm); +} diff --git a/vitest.config.ts b/vitest.config.ts index 81519325a..905ebd81d 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -57,6 +57,8 @@ export default defineConfig((configEnv) => "src/utils/test.ts", "src/utils/test-viewmodel.ts", "src/utils/test-fixtures.ts", + "src/utils/test-matrix-rtc.ts", + "src/matrix-rtc-sdk/generated/**", "playwright/**", ], },